/*******************************************************************************
* 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;
import com.badlogic.gdx.ai.steer.Steerable;
import com.badlogic.gdx.ai.steer.SteeringAcceleration;
import com.badlogic.gdx.ai.steer.SteeringBehavior;
import com.badlogic.gdx.ai.utils.Location;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.g2d.Batch;
import com.badlogic.gdx.graphics.g2d.TextureRegion;
import com.badlogic.gdx.math.MathUtils;
import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.scenes.scene2d.Actor;
import com.badlogic.gdx.utils.Align;
/** A SteeringActor is a scene2d {@link Actor} implementing the {@link Steerable} interface.
*
* @autor davebaol */
public class SteeringActor extends Actor implements Steerable<Vector2> {
private static final SteeringAcceleration<Vector2> steeringOutput = new SteeringAcceleration<Vector2>(new Vector2());
TextureRegion region;
Vector2 position; // like scene2d centerX and centerY, but we need a vector to implement Steerable
Vector2 linearVelocity;
float angularVelocity;
float boundingRadius;
boolean tagged;
float maxLinearSpeed = 100;
float maxLinearAcceleration = 200;
float maxAngularSpeed = 5;
float maxAngularAcceleration = 10;
boolean independentFacing;
SteeringBehavior<Vector2> steeringBehavior;
public SteeringActor (TextureRegion region) {
this(region, false);
}
public SteeringActor (TextureRegion region, boolean independentFacing) {
this.independentFacing = independentFacing;
this.region = region;
this.position = new Vector2();
this.linearVelocity = new Vector2();
this.setBounds(0, 0, region.getRegionWidth(), region.getRegionHeight());
this.boundingRadius = (region.getRegionWidth() + region.getRegionHeight()) / 4f;
this.setOrigin(region.getRegionWidth() * .5f, region.getRegionHeight() * .5f);
}
public TextureRegion getRegion () {
return region;
}
public void setRegion (TextureRegion region) {
this.region = region;
}
@Override
public Vector2 getPosition () {
return position;
}
@Override
public float getOrientation () {
return getRotation() * MathUtils.degreesToRadians;
}
@Override
public void setOrientation (float orientation) {
setRotation(orientation * MathUtils.radiansToDegrees);
}
@Override
public Vector2 getLinearVelocity () {
return linearVelocity;
}
@Override
public float getAngularVelocity () {
return angularVelocity;
}
public void setAngularVelocity (float angularVelocity) {
this.angularVelocity = angularVelocity;
}
@Override
public float getBoundingRadius () {
return boundingRadius;
}
@Override
public boolean isTagged () {
return tagged;
}
@Override
public void setTagged (boolean tagged) {
this.tagged = tagged;
}
@Override
public Location<Vector2> newLocation () {
return new Scene2dLocation();
}
@Override
public float vectorToAngle (Vector2 vector) {
return Scene2dSteeringUtils.vectorToAngle(vector);
}
@Override
public Vector2 angleToVector (Vector2 outVector, float angle) {
return Scene2dSteeringUtils.angleToVector(outVector, angle);
}
@Override
public float getMaxLinearSpeed () {
return maxLinearSpeed;
}
@Override
public void setMaxLinearSpeed (float maxLinearSpeed) {
this.maxLinearSpeed = maxLinearSpeed;
}
@Override
public float getMaxLinearAcceleration () {
return maxLinearAcceleration;
}
@Override
public void setMaxLinearAcceleration (float maxLinearAcceleration) {
this.maxLinearAcceleration = maxLinearAcceleration;
}
@Override
public float getMaxAngularSpeed () {
return maxAngularSpeed;
}
@Override
public void setMaxAngularSpeed (float maxAngularSpeed) {
this.maxAngularSpeed = maxAngularSpeed;
}
@Override
public float getMaxAngularAcceleration () {
return maxAngularAcceleration;
}
@Override
public void setMaxAngularAcceleration (float maxAngularAcceleration) {
this.maxAngularAcceleration = maxAngularAcceleration;
}
@Override
public float getZeroLinearSpeedThreshold () {
return 0.001f;
}
@Override
public void setZeroLinearSpeedThreshold (float value) {
throw new UnsupportedOperationException();
}
public boolean isIndependentFacing () {
return independentFacing;
}
public void setIndependentFacing (boolean independentFacing) {
this.independentFacing = independentFacing;
}
public SteeringBehavior<Vector2> getSteeringBehavior () {
return steeringBehavior;
}
public void setSteeringBehavior (SteeringBehavior<Vector2> steeringBehavior) {
this.steeringBehavior = steeringBehavior;
}
@Override
public void act (float delta) {
position.set(getX(Align.center), getY(Align.center));
if (steeringBehavior != null) {
// Calculate steering acceleration
steeringBehavior.calculateSteering(steeringOutput);
/*
* Here you might want to add a motor control layer filtering steering accelerations.
*
* For instance, a car in a driving game has physical constraints on its movement: it cannot turn while stationary; the
* faster it moves, the slower it can turn (without going into a skid); it can brake much more quickly than it can
* accelerate; and it only moves in the direction it is facing (ignoring power slides).
*/
// Apply steering acceleration
applySteering(steeringOutput, delta);
wrapAround(position, getParent().getWidth(), getParent().getHeight());
setPosition(position.x, position.y, Align.center);
}
super.act(delta);
}
// the display area is considered to wrap around from top to bottom
// and from left to right
protected static void wrapAround (Vector2 pos, float maxX, float maxY) {
if (pos.x > maxX) pos.x = 0.0f;
if (pos.x < 0) pos.x = maxX;
if (pos.y < 0) pos.y = maxY;
if (pos.y > maxY) pos.y = 0.0f;
}
private void applySteering (SteeringAcceleration<Vector2> steering, float time) {
// Update position and linear velocity. Velocity is trimmed to maximum speed
position.mulAdd(linearVelocity, time);
linearVelocity.mulAdd(steering.linear, time).limit(getMaxLinearSpeed());
// Update orientation and angular velocity
if (independentFacing) {
setRotation(getRotation() + (angularVelocity * time) * MathUtils.radiansToDegrees);
angularVelocity += steering.angular * time;
} else {
// If we haven't got any velocity, then we can do nothing.
if (!linearVelocity.isZero(getZeroLinearSpeedThreshold())) {
float newOrientation = vectorToAngle(linearVelocity);
angularVelocity = (newOrientation - getRotation() * MathUtils.degreesToRadians) * time; // this is superfluous if independentFacing is always true
setRotation(newOrientation * MathUtils.radiansToDegrees);
}
}
}
@Override
public void draw (Batch batch, float parentAlpha) {
Color color = getColor();
batch.setColor(color.r, color.g, color.b, parentAlpha);
batch.draw(region, getX(), getY(), getOriginX(), getOriginY(), getWidth(), getHeight(), getScaleX(), getScaleY(),
getRotation());
}
}