/*******************************************************************************
* 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.scene2d.tests;
import com.badlogic.gdx.ai.steer.Steerable;
import com.badlogic.gdx.ai.steer.behaviors.Alignment;
import com.badlogic.gdx.ai.steer.behaviors.BlendedSteering;
import com.badlogic.gdx.ai.steer.behaviors.Cohesion;
import com.badlogic.gdx.ai.steer.behaviors.PrioritySteering;
import com.badlogic.gdx.ai.steer.behaviors.Separation;
import com.badlogic.gdx.ai.steer.behaviors.Wander;
import com.badlogic.gdx.ai.steer.proximities.FieldOfViewProximity;
import com.badlogic.gdx.ai.tests.SteeringBehaviorsTest;
import com.badlogic.gdx.ai.tests.steer.scene2d.Scene2dSteeringTest;
import com.badlogic.gdx.ai.tests.steer.scene2d.SteeringActor;
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.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.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;
import com.badlogic.gdx.utils.Align;
import com.badlogic.gdx.utils.Array;
/** A class to test and experiment with flocking behavior which consists of {@link Separation}, {@link Cohesion} and
* {@link Alignment}.
*
* @autor davebaol */
public class Scene2dFlockingTest extends Scene2dSteeringTest {
Array<SteeringActor> characters;
boolean drawDebug;
ShapeRenderer shapeRenderer;
Array<BlendedSteering<Vector2>> blendedSteerings;
FieldOfViewProximity<Vector2> char0Proximity;
Array<FieldOfViewProximity<Vector2>> proximities;
float separationDecayCoefficient;
public Scene2dFlockingTest (SteeringBehaviorsTest container) {
super(container, "Flocking");
}
@Override
public void create () {
super.create();
drawDebug = true;
shapeRenderer = new ShapeRenderer();
separationDecayCoefficient = 500;
characters = new Array<SteeringActor>();
blendedSteerings = new Array<BlendedSteering<Vector2>>();
proximities = new Array<FieldOfViewProximity<Vector2>>();
for (int i = 0; i < 60; i++) {
SteeringActor character = new SteeringActor(container.greenFish, false);
character.setPosition(MathUtils.random(container.stageWidth), MathUtils.random(container.stageHeight), Align.center);
character.setMaxLinearSpeed(70);
character.setMaxLinearAcceleration(400); //
character.setMaxAngularAcceleration(0);
character.setMaxAngularSpeed(5);
FieldOfViewProximity<Vector2> proximity = new FieldOfViewProximity<Vector2>(character, characters, 140,
270 * MathUtils.degreesToRadians);
proximities.add(proximity);
if (i == 0) char0Proximity = proximity;
Alignment<Vector2> groupAlignmentSB = new Alignment<Vector2>(character, proximity);
Cohesion<Vector2> groupCohesionSB = new Cohesion<Vector2>(character, proximity);
Separation<Vector2> groupSeparationSB = new Separation<Vector2>(character, proximity) {
@Override
public float getDecayCoefficient () {
// We want all the agents to use the same decay coefficient
return separationDecayCoefficient;
}
@Override
public Separation<Vector2> setDecayCoefficient (float decayCoefficient) {
separationDecayCoefficient = decayCoefficient;
return this;
}
};
BlendedSteering<Vector2> blendedSteering = new BlendedSteering<Vector2>(character) //
.add(groupAlignmentSB, .2f) //
.add(groupCohesionSB, .06f) //
.add(groupSeparationSB, 1.7f);
blendedSteerings.add(blendedSteering);
// TODO set more proper values
Wander<Vector2> wanderSB = new Wander<Vector2>(character) //
// Don't use Face internally because independent facing is off
.setFaceEnabled(false) //
// No need to call setAlignTolerance, setDecelerationRadius and setTimeToTarget because we don't use internal Face
.setWanderOffset(60) //
.setWanderOrientation(10) //
.setWanderRadius(40) //
.setWanderRate(MathUtils.PI2 * 4);
PrioritySteering<Vector2> prioritySteeringSB = new PrioritySteering<Vector2>(character, 0.0001f) //
.add(blendedSteering) //
.add(wanderSB);
character.setSteeringBehavior(prioritySteeringSB);
testTable.addActor(character);
characters.add(character);
}
inputProcessor = null;
Table detailTable = new Table(container.skin);
detailTable.row();
final Label alignmentWeightLabel = new Label("Alignment Weight [" + blendedSteerings.get(0).get(0).getWeight() + "]",
container.skin);
detailTable.add(alignmentWeightLabel);
detailTable.row();
Slider alignmentWeight = new Slider(0, 2, .01f, false, container.skin);
alignmentWeight.setValue(blendedSteerings.get(0).get(0).getWeight());
alignmentWeight.addListener(new ChangeListener() {
@Override
public void changed (ChangeEvent event, Actor actor) {
Slider slider = (Slider)actor;
for (int i = 0; i < blendedSteerings.size; i++)
blendedSteerings.get(i).get(0).setWeight(slider.getValue());
alignmentWeightLabel.setText("Alignment Weight [" + blendedSteerings.get(0).get(0).getWeight() + "]");
}
});
detailTable.add(alignmentWeight);
detailTable.row();
final Label cohesionWeightLabel = new Label("Cohesion Weight [" + blendedSteerings.get(0).get(1).getWeight() + "]",
container.skin);
detailTable.add(cohesionWeightLabel);
detailTable.row();
Slider cohesionWeight = new Slider(0, 2, .01f, false, container.skin);
cohesionWeight.setValue(blendedSteerings.get(0).get(1).getWeight());
cohesionWeight.addListener(new ChangeListener() {
@Override
public void changed (ChangeEvent event, Actor actor) {
Slider slider = (Slider)actor;
for (int i = 0; i < blendedSteerings.size; i++)
blendedSteerings.get(i).get(1).setWeight(slider.getValue());
cohesionWeightLabel.setText("Cohesion Weight [" + blendedSteerings.get(0).get(1).getWeight() + "]");
}
});
detailTable.add(cohesionWeight);
detailTable.row();
final Label separationWeightLabel = new Label("Separation Weight [" + blendedSteerings.get(0).get(2).getWeight() + "]",
container.skin);
detailTable.add(separationWeightLabel);
detailTable.row();
Slider separationWeight = new Slider(0, 2, .01f, false, container.skin);
separationWeight.setValue(blendedSteerings.get(0).get(2).getWeight());
separationWeight.addListener(new ChangeListener() {
@Override
public void changed (ChangeEvent event, Actor actor) {
Slider slider = (Slider)actor;
for (int i = 0; i < blendedSteerings.size; i++)
blendedSteerings.get(i).get(2).setWeight(slider.getValue());
separationWeightLabel.setText("Separation Weight [" + blendedSteerings.get(0).get(2).getWeight() + "]");
}
});
detailTable.add(separationWeight);
detailTable.row();
final Label separationDecayCoeffLabel = new Label("Separation Decay Coeff.[" + separationDecayCoefficient + "]",
container.skin);
detailTable.add(separationDecayCoeffLabel);
detailTable.row();
Slider separationDecayCoeff = new Slider(1, 5000, 1, false, container.skin);
separationDecayCoeff.setValue(separationDecayCoefficient);
separationDecayCoeff.addListener(new ChangeListener() {
@Override
public void changed (ChangeEvent event, Actor actor) {
Slider slider = (Slider)actor;
separationDecayCoefficient = slider.getValue();
separationDecayCoeffLabel.setText("Separation Decay Coeff.[" + separationDecayCoefficient + "]");
}
});
detailTable.add(separationDecayCoeff);
detailTable.row();
addSeparator(detailTable);
detailTable.row();
final Label labelProximityRadius = new Label("Proximity Radius [" + proximities.get(0).getRadius() + "]", container.skin);
detailTable.add(labelProximityRadius);
detailTable.row();
Slider proximityRadius = new Slider(0, 500, 1, false, container.skin);
proximityRadius.setValue(proximities.get(0).getRadius());
proximityRadius.addListener(new ChangeListener() {
@Override
public void changed (ChangeEvent event, Actor actor) {
Slider slider = (Slider)actor;
for (int i = 0; i < proximities.size; i++)
proximities.get(i).setRadius(slider.getValue());
labelProximityRadius.setText("Proximity Radius [" + slider.getValue() + "]");
}
});
detailTable.add(proximityRadius);
detailTable.row();
final Label labelProximityAngle = new Label("Proximity Angle [" + proximities.get(0).getAngle() * MathUtils.radiansToDegrees + "]", container.skin);
detailTable.add(labelProximityAngle);
detailTable.row();
Slider proximityAngle = new Slider(0, 360, 1, false, container.skin);
proximityAngle.setValue(proximities.get(0).getAngle() * MathUtils.radiansToDegrees);
proximityAngle.addListener(new ChangeListener() {
@Override
public void changed (ChangeEvent event, Actor actor) {
Slider slider = (Slider)actor;
for (int i = 0; i < proximities.size; i++)
proximities.get(i).setAngle(slider.getValue() * MathUtils.degreesToRadians);
labelProximityAngle.setText("Proximity Angle [" + slider.getValue() + "]");
}
});
detailTable.add(proximityAngle);
detailTable.row();
final Label labelMaxLinAcc = new Label("Max.Linear Acc.[" + characters.get(0).getMaxLinearAcceleration() + "]",
container.skin);
detailTable.add(labelMaxLinAcc);
detailTable.row();
Slider maxLinAcc = new Slider(0, 500, 1, false, container.skin);
maxLinAcc.setValue(characters.get(0).getMaxLinearAcceleration());
maxLinAcc.addListener(new ChangeListener() {
@Override
public void changed (ChangeEvent event, Actor actor) {
Slider slider = (Slider)actor;
for (int i = 0; i < characters.size; i++)
characters.get(i).setMaxLinearAcceleration(slider.getValue());
labelMaxLinAcc.setText("Max.Linear Acc.[" + slider.getValue() + "]");
}
});
detailTable.add(maxLinAcc);
detailTable.row();
final Label labelMaxLinSpeed = new Label("Max.Lin.Speed [" + characters.get(0).getMaxLinearSpeed() + "]", container.skin);
detailTable.add(labelMaxLinSpeed);
detailTable.row();
Slider maxLinSpeed = new Slider(0, 500, 1, false, container.skin);
maxLinSpeed.setValue(characters.get(0).getMaxLinearSpeed());
maxLinSpeed.addListener(new ChangeListener() {
@Override
public void changed (ChangeEvent event, Actor actor) {
Slider slider = (Slider)actor;
for (int i = 0; i < characters.size; i++)
characters.get(i).setMaxLinearSpeed(slider.getValue());
labelMaxLinSpeed.setText("Max.Lin.Speed [" + slider.getValue() + "]");
}
});
detailTable.add(maxLinSpeed);
detailTable.row();
addSeparator(detailTable);
detailTable.row();
CheckBox debug = new CheckBox("Draw Proximity", 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);
detailWindow = createDetailWindow(detailTable);
}
@Override
public void draw () {
if (drawDebug) {
Steerable<Vector2> steerable = characters.get(0);
shapeRenderer.begin(ShapeType.Line);
shapeRenderer.setColor(0, 1, 0, 1);
float angle = char0Proximity.getAngle() * MathUtils.radiansToDegrees;
shapeRenderer.arc(steerable.getPosition().x, steerable.getPosition().y, char0Proximity.getRadius(),
steerable.getOrientation() * MathUtils.radiansToDegrees - angle / 2f + 90f, angle);
shapeRenderer.end();
}
}
@Override
public void dispose () {
super.dispose();
shapeRenderer.dispose();
}
}