Java tutorial
/******************************************************************************* * Copyright 2011 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; import com.badlogic.gdx.Gdx; 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.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.physics.box2d.Body; /** A steering entity for box2d physics engine. * * @author davebaol */ public class Box2dSteeringEntity implements Steerable<Vector2> { TextureRegion region; Body body; float boundingRadius; boolean tagged; float maxLinearSpeed; float maxLinearAcceleration; float maxAngularSpeed; float maxAngularAcceleration; boolean independentFacing; protected SteeringBehavior<Vector2> steeringBehavior; private static final SteeringAcceleration<Vector2> steeringOutput = new SteeringAcceleration<Vector2>( new Vector2()); public Box2dSteeringEntity(TextureRegion region, Body body, boolean independentFacing, float boundingRadius) { this.region = region; this.body = body; this.independentFacing = independentFacing; this.boundingRadius = boundingRadius; this.tagged = false; body.setUserData(this); } public TextureRegion getRegion() { return region; } public void setRegion(TextureRegion region) { this.region = region; } public Body getBody() { return body; } public void setBody(Body body) { this.body = body; } public boolean isIndependentFacing() { return independentFacing; } public void setIndependentFacing(boolean independentFacing) { this.independentFacing = independentFacing; } @Override public Vector2 getPosition() { return body.getPosition(); } @Override public float getOrientation() { return body.getAngle(); } @Override public Vector2 getLinearVelocity() { return body.getLinearVelocity(); } @Override public float getAngularVelocity() { return body.getAngularVelocity(); } @Override public float getBoundingRadius() { return boundingRadius; } @Override public boolean isTagged() { return tagged; } @Override public void setTagged(boolean tagged) { this.tagged = tagged; } @Override public Vector2 newVector() { return new Vector2(); } @Override public float vectorToAngle(Vector2 vector) { return (float) Math.atan2(-vector.x, vector.y); } @Override public Vector2 angleToVector(Vector2 outVector, float angle) { outVector.x = -(float) Math.sin(angle); outVector.y = (float) Math.cos(angle); return outVector; } public SteeringBehavior<Vector2> getSteeringBehavior() { return steeringBehavior; } public void setSteeringBehavior(SteeringBehavior<Vector2> steeringBehavior) { this.steeringBehavior = steeringBehavior; } public void update(float deltaTime) { 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, deltaTime); } wrapAround(Box2dSteeringTest.pixelsToMeters(Gdx.graphics.getWidth()), Box2dSteeringTest.pixelsToMeters(Gdx.graphics.getHeight())); } protected void applySteering(SteeringAcceleration<Vector2> steering, float deltaTime) { boolean anyAccelerations = false; // Update position and linear velocity. if (!steeringOutput.linear.isZero()) { Vector2 force = steeringOutput.linear.scl(deltaTime); body.applyForceToCenter(force, true); anyAccelerations = true; } // Update orientation and angular velocity if (isIndependentFacing()) { if (steeringOutput.angular != 0) { body.applyTorque(steeringOutput.angular * deltaTime, true); anyAccelerations = true; } } else { // If we haven't got any velocity, then we can do nothing. Vector2 linVel = getLinearVelocity(); if (!linVel.isZero(MathUtils.FLOAT_ROUNDING_ERROR)) { float newOrientation = vectorToAngle(linVel); body.setAngularVelocity((newOrientation - getAngularVelocity()) * deltaTime); // this is superfluous if independentFacing is always true body.setTransform(body.getPosition(), newOrientation); } } if (anyAccelerations) { // body.activate(); // TODO: // Looks like truncating speeds here after applying forces doesn't work as expected. // We should likely cap speeds form inside an InternalTickCallback, see // http://www.bulletphysics.org/mediawiki-1.5.8/index.php/Simulation_Tick_Callbacks // Cap the linear speed Vector2 velocity = body.getLinearVelocity(); float currentSpeedSquare = velocity.len2(); float maxLinearSpeed = getMaxLinearSpeed(); if (currentSpeedSquare > maxLinearSpeed * maxLinearSpeed) { body.setLinearVelocity(velocity.scl(maxLinearSpeed / (float) Math.sqrt(currentSpeedSquare))); } // Cap the angular speed float maxAngVelocity = getMaxAngularSpeed(); if (body.getAngularVelocity() > maxAngVelocity) { body.setAngularVelocity(maxAngVelocity); } } } // the display area is considered to wrap around from top to bottom // and from left to right protected void wrapAround(float maxX, float maxY) { float k = Float.POSITIVE_INFINITY; Vector2 pos = body.getPosition(); if (pos.x > maxX) k = pos.x = 0.0f; if (pos.x < 0) k = pos.x = maxX; if (pos.y < 0) k = pos.y = maxY; if (pos.y > maxY) k = pos.y = 0.0f; if (k != Float.POSITIVE_INFINITY) body.setTransform(pos, body.getAngle()); } public void draw(Batch batch) { Vector2 pos = body.getPosition(); float w = region.getRegionWidth(); float h = region.getRegionHeight(); float ox = w / 2f; float oy = h / 2f; batch.draw(region, // Box2dSteeringTest.metersToPixels(pos.x) - ox, Box2dSteeringTest.metersToPixels(pos.y) - oy, // ox, oy, // w, h, // 1, 1, // body.getAngle() * MathUtils.radiansToDegrees); // } // // Limiter implementation // @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; } }