Last active
February 11, 2016 14:57
-
-
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..
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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