Skip to content

Instantly share code, notes, and snippets.

@7yl4r
Last active February 11, 2016 14:57
Show Gist options
  • Save 7yl4r/efae43aa19a5d1c4eb5a to your computer and use it in GitHub Desktop.
Save 7yl4r/efae43aa19a5d1c4eb5a to your computer and use it in GitHub Desktop.
makes default libgdx camera follow the character around the scene while avoiding obstacles by applying a repulsing force from each obstacle and a pull towards the player..
package tests;
import com.badlogic.gdx.graphics.PerspectiveCamera;
import com.badlogic.gdx.math.Vector3;
import java.util.ArrayList;
/**
* Makes default libgdx camera follow the character around the scene
* while avoiding a list of obstacles.
*/
public class AvoiderCamera {
private final short MAX_OBSTACLES = 9;
private PerspectiveCamera camera;
public AvoiderCamera(PerspectiveCamera camera){
this.camera = camera;
}
public void update(float delta, Vector3 target, ArrayList<Obstacle> obstacleList) {
System.out.println("cam pos: " + camera.position);
ArrayList<Obstacle> obstacles = getNearestObstaclesTo(camera.position, obstacleList);
Vector3 push = new Vector3();
float idealDist = 10f; // ideal distance camera is from object (actual distance is ~75% of this distance
// add invisible objects above and below target to limit extreme-angle views
final int angleLimiterSize = 5;
if (angleLimiterSize > 0) {
obstacles.add(new Obstacle(new Vector3(target).add(0, idealDist*.75f, 0), angleLimiterSize));
obstacles.add(new Obstacle(new Vector3(target).add(0, -idealDist*.75f, 0), angleLimiterSize));
}
// add pushes from obstacles
for (Obstacle obs : obstacles) {
push.add(obs.getPushFrom(camera.position));
System.out.println("push" + obs.getPushFrom(camera.position));
}
// push the camera into spherical surface around target
float dist = camera.position.dst(target);
System.out.println("cam dist: " + dist);
push.add(target.cpy().sub(camera.position).nor().scl(dist/idealDist)); // push towards target
push.add(camera.position.cpy().sub(target).nor().scl((idealDist / (2f * dist)))); // push away from target
if (push.len() > .1) { // cutoff to avoid jitter
System.out.println("pushing camera " + push);
camera.position.add(push);
}
camera.lookAt(target);
camera.up.set(Vector3.Y); // lock "up" to Y direction
camera.update();
System.out.println("new camera position: " + camera.position);
}
private ArrayList<Obstacle> getNearestObstaclesTo(Vector3 targetPos, ArrayList<Obstacle> obstacleList){
// return list of nearest n obstacles. n = MAX_OBSTACLES
// Where n is the number of obstacles to consider in n-body calculations.
// Use a larger n for more accurate obstacle avoidance, lower for better performance.
ArrayList<Obstacle> obsList = new ArrayList<Obstacle>();
for (Obstacle obs : obstacleList){
if (obsList.size() < MAX_OBSTACLES){
obsList.add(obs);
} else {
// check if closer than other obstacles
for (int i = 0; i < obsList.size(); i++) {
Obstacle closeObs = obsList.get(i);
if (obs.position.dst2(targetPos) < closeObs.position.dst2(targetPos)){
obsList.set(i, obs);
break;
}
}
}
}
return obsList;
}
}
package tests;
import com.badlogic.gdx.math.Vector3;
/**
* defines a spherical obstacle
*/
public class Obstacle {
public float force; // amount of force to repel camera (larger for objects with larger radius)
public Vector3 position; // location of object center
public Obstacle(Vector3 position, float radius){
this.force = getForceFromRadius(radius);
this.position = position;
}
public Vector3 getPushFrom(Vector3 pos){
// returns push on given position
float dist = position.dst(pos);
if (dist < .0000001f){
dist = .0000001f;
}
// push is proporitional to 1/dist^2, can also try just *dist, *dist*dist*dist, etc
return pos.cpy().sub(position).nor().scl(force / (dist * dist));
}
private float getForceFromRadius(float rad){
return rad; // TODO: scale this according to your world sizing
}
}
import com.badlogic.gdx.*;
import com.badlogic.gdx.backends.lwjgl.LwjglApplication;
import com.badlogic.gdx.backends.lwjgl.LwjglApplicationConfiguration;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.GL30;
import com.badlogic.gdx.graphics.PerspectiveCamera;
import com.badlogic.gdx.graphics.VertexAttributes;
import com.badlogic.gdx.graphics.g3d.*;
import com.badlogic.gdx.graphics.g3d.attributes.ColorAttribute;
import com.badlogic.gdx.graphics.g3d.environment.DirectionalLight;
import com.badlogic.gdx.graphics.g3d.utils.ModelBuilder;
import com.badlogic.gdx.math.Vector3;
import java.util.ArrayList;
/**
* Created by 7yl4r on 2/9/2016.
*/
public class barnTest {
public static void main (String[] arg) {
LwjglApplicationConfiguration config = new LwjglApplicationConfiguration();
config.width = 1080;
config.height = 750;
config.useGL30 = true;
new LwjglApplication(new Game() {
AvoiderCamera cameraController;
PerspectiveCamera cam;
ModelBatch modelBatch;
Model playerModel;
Model obstacleModel;
ModelInstance[] obstacles;
ModelInstance instance;
public Environment environment;
ArrayList<Obstacle> mockList = new ArrayList<Obstacle>();
@Override
public void create() {
environment = new Environment();
environment.set(new ColorAttribute(ColorAttribute.AmbientLight, 0.4f, 0.4f, 0.4f, 1f));
environment.add(new DirectionalLight().set(0.8f, 0.8f, 0.8f, -1f, -0.8f, -0.2f));
modelBatch = new ModelBatch();
cam = new PerspectiveCamera(67, Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
cam.position.set(10f, 10f, 10f);
cam.lookAt(0,0,0);
cam.near = 1f;
cam.far = 300f;
cam.update();
final ModelBuilder modelBuilder = new ModelBuilder();
playerModel = modelBuilder.createBox(1f, 1f, 1f,
new Material(ColorAttribute.createDiffuse(Color.GREEN)),
VertexAttributes.Usage.Position | VertexAttributes.Usage.Normal);
obstacleModel = modelBuilder.createSphere(1f, 1f, 1f, 10, 10,
new Material(ColorAttribute.createDiffuse(Color.RED)),
VertexAttributes.Usage.Position | VertexAttributes.Usage.Normal);
instance = new ModelInstance(playerModel);
// // random obstacles:
// mockList.add(new Obstacle(new Vector3(1, 0, 0), 1)); // small obstacle
// mockList.add(new Obstacle(new Vector3(10, 5, 0), 10)); // big obstacle
// mockList.add(new Obstacle(new Vector3(-10,0,0), 5)); // medium obstacle
// mockList.add(new Obstacle(new Vector3(-10,-10,0), 3)); // medium obstacle
// mockList.add(new Obstacle(new Vector3(5,5,5), 2)); // medium obstacle
// obstacle grid
final int SIZE = 10;
final float SPACE = 10f;
final float RAD = 3f;
for (int x = -SIZE; x < SIZE; x++){
for (int y = -SIZE; y < SIZE; y++){
for (int z = -SIZE; z < SIZE; z++) {
mockList.add(new Obstacle(new Vector3(SPACE * x, SPACE * y, SPACE * z), RAD));
}
}
}
obstacles = new ModelInstance[mockList.size()];
for (int i = 0; i < mockList.size(); i++){
obstacles[i] = new ModelInstance(obstacleModel);
obstacles[i].transform.translate(mockList.get(i).position);
float scl = mockList.get(i).force;
obstacles[i].transform.scale(scl,scl,scl);
}
cameraController = new AvoiderCamera(cam);
setScreen(new Screen() {
@Override
public void show() {
}
@Override
public void render(float delta) {
cameraController.update(delta,
instance.transform.getTranslation(new Vector3()),
mockList
);
Gdx.gl.glViewport(0, 0, Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
Gdx.gl.glClear(GL30.GL_COLOR_BUFFER_BIT | GL30.GL_DEPTH_BUFFER_BIT);
modelBatch.begin(cam);
modelBatch.render(instance);
for (ModelInstance inst : obstacles) {
modelBatch.render(inst);
}
modelBatch.end();
// movement control
Vector3 dir = new Vector3();
float SPD = .2f;
if (Gdx.input.isKeyPressed(Input.Keys.W)) {
dir.add(0, 0, SPD);
} else if (Gdx.input.isKeyPressed(Input.Keys.S)) {
dir.add(0, 0, -SPD);
}
if (Gdx.input.isKeyPressed(Input.Keys.A)) {
dir.add(-SPD, 0, 0);
} else if (Gdx.input.isKeyPressed(Input.Keys.D)) {
dir.add(SPD, 0, 0);
}
if (Gdx.input.isKeyPressed(Input.Keys.UP)){
dir.add(0,SPD,0);
} else if (Gdx.input.isKeyPressed(Input.Keys.DOWN)){
dir.add(0,-SPD,0);
}
if (Gdx.input.isKeyPressed(Input.Keys.ENTER)){
instance.transform.setToTranslation(new Vector3(0,0,0));
}
instance.transform.translate(dir);
}
@Override
public void resize(int width, int height) {
}
@Override
public void pause() {
}
@Override
public void resume() {
}
@Override
public void hide() {
}
@Override
public void dispose() {
modelBatch.dispose();
playerModel.dispose();
}
});
}
}, config);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment