com.zombie.game.actors.SteeringActor.java Source code

Java tutorial

Introduction

Here is the source code for com.zombie.game.actors.SteeringActor.java

Source

/*******************************************************************************
 * Copyright 2014 See AUTHORS file.
 * <p/>
 * 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
 * <p/>
 * http://www.apache.org/licenses/LICENSE-2.0
 * <p/>
 * 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.zombie.game.actors;

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.GL20;
import com.badlogic.gdx.graphics.g2d.Batch;
import com.badlogic.gdx.graphics.g2d.TextureRegion;
import com.badlogic.gdx.graphics.g3d.Material;
import com.badlogic.gdx.graphics.g3d.ModelInstance;
import com.badlogic.gdx.graphics.g3d.attributes.BlendingAttribute;
import com.badlogic.gdx.graphics.g3d.attributes.FloatAttribute;
import com.badlogic.gdx.graphics.g3d.attributes.IntAttribute;
import com.badlogic.gdx.graphics.g3d.utils.AnimationController;
import com.badlogic.gdx.math.MathUtils;
import com.badlogic.gdx.math.Matrix4;
import com.badlogic.gdx.math.Quaternion;
import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.math.Vector3;
import com.badlogic.gdx.scenes.scene2d.Actor;
import com.badlogic.gdx.scenes.scene2d.Event;
import com.badlogic.gdx.scenes.scene2d.InputEvent;
import com.badlogic.gdx.utils.Align;
import com.zombie.game.events.ActorHighlightedEvent;
import com.zombie.game.steering.Scene2dLocation;
import com.zombie.game.steering.Scene2dSteeringUtils;

/** A SteeringActor is a scene2d {@link Actor} implementing the {@link Steerable} interface.
 *
 * @autor davebaol */
public class SteeringActor extends Actor implements AnimationController.AnimationListener, Steerable<Vector2> {

    private static final SteeringAcceleration<Vector2> steeringOutput = new SteeringAcceleration<Vector2>(
            new Vector2());

    ModelInstance modelInstance;

    /** Animation controller */
    protected AnimationController animationController;

    TextureRegion region;

    Vector2 position; // like scene2d centerX and centerY, but we need a vector to implement Steerable

    Quaternion direction = new Quaternion(Vector3.Y, 0);
    Quaternion perspective = new Quaternion();
    float transform[] = new float[16];

    Vector2 linearVelocity;
    float angularVelocity;
    float boundingRadius;
    boolean tagged;
    boolean active = false;
    Mob mob = null;

    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 ModelInstance getModelInstance() {
        return modelInstance;
    }

    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);
    }

    protected void setRandomOrientation(SteeringActor character) {
        float orientation = MathUtils.random(-MathUtils.PI, MathUtils.PI);
        character.setOrientation(orientation);
        if (!character.isIndependentFacing()) {
            // Set random initial non-zero linear velocity since independent facing is off
            character.angleToVector(character.getLinearVelocity(), orientation)
                    .scl(character.getMaxLinearSpeed() / 5);
        }
    }

    @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 modelInstance, 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);
    }

    @Override
    public void setPosition(float x, float y, int alignment) {
        super.setPosition(x, y, alignment);
    }

    public void setModelInstance(ModelInstance modelInstance) {
        this.modelInstance = modelInstance;
        this.transform = modelInstance.transform.val;
        for (Material m : modelInstance.materials) {
            m.set(new IntAttribute(IntAttribute.CullFace, GL20.GL_NONE));
            m.set(new FloatAttribute(FloatAttribute.AlphaTest, 0.5f));
            m.set(new BlendingAttribute(GL20.GL_SRC_ALPHA, GL20.GL_ONE_MINUS_SRC_ALPHA));
        }
        //Get animations if any
        if (modelInstance.animations.size > 0) {
            animationController = new AnimationController(modelInstance);
            animationController.allowSameAnimation = true;
            animationController.animate(modelInstance.animations.get(0).id, -1, 1f, this, 1f);
        }
    }

    // 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());
    }

    @Override
    public boolean fire(Event event) {
        if (event instanceof InputEvent) {
            InputEvent.Type t = ((InputEvent) event).getType();
            event.stop();
            ActorHighlightedEvent actorEvent = new ActorHighlightedEvent();
            actorEvent.setSteeringActor(this);
            if (t != InputEvent.Type.exit) {
                this.active = true;
                return super.fire(actorEvent);
            }
            this.active = false;
        }
        return super.fire(event);
    }

    public boolean isActive() {
        return active;
    }

    public AnimationController getAnimationController() {
        return animationController;
    }

    public Mob getMob() {
        return mob;
    }

    public void setMob(Mob mob) {
        this.mob = mob;
    }

    public void deselect() {
        mob = null;
    }

    @Override
    public void onEnd(AnimationController.AnimationDesc animation) {
    }

    @Override
    public void onLoop(AnimationController.AnimationDesc animation) {
    }

    /** Called when the actor's position has been changed. */
    protected void positionChanged() {
        transform[Matrix4.M03] = getPosition().x;
        transform[Matrix4.M13] = getPosition().y;
    }

    /** Called when the actor's size has been changed. */
    protected void sizeChanged() {
    }

    /** Called when the actor's rotation has been changed. */
    protected void rotationChanged() {
        direction.set(Vector3.Y, getRotation() + 180);
        perspective.set(0.5f, 0f, 0f, 0.8660254f);
        perspective.mul(direction);
        perspective.toMatrix(transform);
    }
}