package com.mygdx.game;
import com.badlogic.gdx.ApplicationListener;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Input;
import com.badlogic.gdx.Input.Keys;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.OrthographicCamera;
import com.badlogic.gdx.graphics.g2d.BitmapFont;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.math.Vector3;
import com.badlogic.gdx.physics.box2d.Body;
import com.badlogic.gdx.physics.box2d.BodyDef;
import com.badlogic.gdx.physics.box2d.BodyDef.BodyType;
import com.badlogic.gdx.physics.box2d.Box2DDebugRenderer;
import com.badlogic.gdx.physics.box2d.CircleShape;
import com.badlogic.gdx.physics.box2d.Contact;
import com.badlogic.gdx.physics.box2d.EdgeShape;
import com.badlogic.gdx.physics.box2d.Fixture;
import com.badlogic.gdx.physics.box2d.PolygonShape;
import com.badlogic.gdx.physics.box2d.World;
import com.badlogic.gdx.physics.box2d.WorldManifold;
import com.badlogic.gdx.utils.Array;
import com.badlogic.gdx.utils.TimeUtils;
import com.mygdx.game.utils.GdxTest;
public class Box2DCharacterControllerTest extends GdxTest implements ApplicationListener {
final static float MAX_VELOCITY = 14f;
boolean jump = false;
World world;
Body player;
Fixture playerPhysicsFixture;// 夹具,将形状附加到物体上?
Fixture playerSensorFixture;
OrthographicCamera cam;
Box2DDebugRenderer renderer;// 把Box2D的边缘线显示出来?
Array<Platform> platforms = new Array<Platform>();
Platform groundedPlatform = null;
float stillTime = 0;
long lastGroundTime = 0;
SpriteBatch batch;
BitmapFont font;
float accum = 0;
float TICK = 1 / 60f;//每秒60帧
@Override
public void create () {
//world = new World(new Vector2(0, -40), true);
world = new World(new Vector2(0, -10), true);
renderer = new Box2DDebugRenderer();
cam = new OrthographicCamera(28, 20);// 28米宽20米高
createWorld();
Gdx.input.setInputProcessor(this);
batch = new SpriteBatch();
font = new BitmapFont(Gdx.files.internal("data/arial-15.fnt"), false);
}
@Override
public void dispose () {
world.dispose();
renderer.dispose();
batch.dispose();
font.dispose();
}
private void createWorld () {
//float y1 = 1;// 创建平直路面
float y1 = (float)Math.random() * 0.1f + 1;// 创建不规则路面
float y2 = y1;
for (int i = 0; i < 50; i++) {// 创建50单位长度的地面
// 起点:-50米处,终点 -50+49*2+2 就是50米处 y1 y2 1米高的地方
Body ground = createEdge(BodyType.StaticBody, (-50 + i * 2), (y1), (-50 + i * 2 + 2), (y2), 0);// createEdge 就是创建一条线?
y1 = y2;
//y2 = 1;
y2=(float)Math.random() + 1;// 要么高要么低,在1米高处差上下最多1米
}
Body box = createBox(BodyType.StaticBody, 1, 1, 0);// 显示为绿色
//Body box = createBox(BodyType.StaticBody, 5, 5, 0);
box.setTransform(30, 3, 0);
box = createBox(BodyType.StaticBody, 1.2f, 1.2f, 0);
//box = createBox(BodyType.StaticBody, 5f, 5f, 0);
box.setTransform(5, 2.4f, 0);
player = createPlayer();
player.setTransform(-40.0f, 4.0f, 0);
player.setFixedRotation(true);// 这句斜杠掉player各种打转,然后不会跳了
for (int i = 0; i < 20; i++) {// DynamicBody都是红色
box = createBox(BodyType.DynamicBody, (float)Math.random(), (float)Math.random(), 3);
box.setTransform((float)Math.random() * 10f - (float)Math.random() * 10f, (float)Math.random() * 10 + 6,
(float)(Math.random() * 2 * Math.PI));
}
for (int i = 0; i < 20; i++) {
Body circle = createCircle(BodyType.DynamicBody, (float)Math.random() * 0.5f, 3);
circle.setTransform((float)Math.random() * 10f - (float)Math.random() * 10f, (float)Math.random() * 10 + 6,
(float)(Math.random() * 2 * Math.PI));
}
platforms.add(new CirclePlatform(-24, -5, 10, (float)Math.PI / 4));// 大转盘
platforms.add(new CirclePlatform(20, 3, 5, (float)Math.PI ));// 自己加一个小的
platforms.add(new MovingPlatform(-2, 3, 2, 0.5f, 2, 0, (float)Math.PI / 10f, 4));
platforms.add(new MovingPlatform(17, 2, 5, 0.5f, 2, 0, 0, 5));
//platforms.add(new MovingPlatform(-7, 5, 2, 0.5f, -2, 2, 0, 8));
platforms.add(new MovingPlatform(-7, 5, 2, 0.5f, -2, 1, 0, 16));
// platforms.add(new MovingPlatform(40, 3, 20, 0.5f, 0, 2, 5));
}
Body createBox (BodyType type, float width, float height, float density) {
BodyDef def = new BodyDef();
def.type = type;
Body box = world.createBody(def);
PolygonShape poly = new PolygonShape();
poly.setAsBox(width, height);
box.createFixture(poly, density);
poly.dispose();// 不要忘记
return box;
}
private Body createEdge (BodyType type, float x1, float y1, float x2, float y2, float density) {
BodyDef def = new BodyDef();
def.type = type;
Body box = world.createBody(def);
EdgeShape poly = new EdgeShape();// 创建一条线?
poly.set(new Vector2(0, 0), new Vector2(x2 - x1, y2 - y1));// 长度从x1y1到x2y2那么长
box.createFixture(poly, density);// density为零就是不动的物体如地面
box.setTransform(x1, y1, 0);
poly.dispose();
return box;
}
Body createCircle (BodyType type, float radius, float density) {
BodyDef def = new BodyDef();
def.type = type;
Body box = world.createBody(def);
CircleShape poly = new CircleShape();
poly.setRadius(radius);
box.createFixture(poly, density);
poly.dispose();
return box;
}
private Body createPlayer () {
BodyDef def = new BodyDef();
def.type = BodyType.DynamicBody;
Body box = world.createBody(def);
PolygonShape poly = new PolygonShape();
poly.setAsBox(0.45f, 1.4f);
playerPhysicsFixture = box.createFixture(poly, 1);
poly.dispose();
CircleShape circle = new CircleShape();
circle.setRadius(0.45f);
circle.setPosition(new Vector2(0, -1.4f));
playerSensorFixture = box.createFixture(circle, 0);
circle.dispose();
box.setBullet(true);// 碰撞检测更精确?
return box;
}
@Override
public void resume () {
}
@Override
public void render () {
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
cam.position.set(player.getPosition().x, player.getPosition().y, 0);
cam.update();
renderer.render(world, cam.combined);
Vector2 vel = player.getLinearVelocity();
Vector2 pos = player.getPosition();
boolean grounded = isPlayerGrounded(Gdx.graphics.getDeltaTime());
if (grounded) {
lastGroundTime = TimeUtils.nanoTime();
} else {
//if (TimeUtils.nanoTime() - lastGroundTime < 100000000) {
if (TimeUtils.nanoTime() - lastGroundTime < 100000000) {// 让grounded有一个延时
//Gdx.app.log("Jacob","nanotime: "+TimeUtils.nanoTime());
//Gdx.app.log("Jacob","lastgroundtime: "+lastGroundTime);
grounded = true;// 这里是什么意思?意思是只要是在100000000这个时间范围内都还是算落地了?
}
}
// cap max velocity on x
if (Math.abs(vel.x) > MAX_VELOCITY) {
vel.x = Math.signum(vel.x) * MAX_VELOCITY;// signum 返回参数的正负号
player.setLinearVelocity(vel.x, vel.y);
}
// calculate stilltime & damp
if (!Gdx.input.isKeyPressed(Keys.A) && !Gdx.input.isKeyPressed(Keys.D)) {
stillTime += Gdx.graphics.getDeltaTime();
//player.setLinearVelocity(vel.x * 0.9f, vel.y);// 逐渐减速?
//player.setLinearVelocity(vel.x * 1.1f, vel.y);
player.setLinearVelocity(vel.x * 0.01f, vel.y);// 逐渐减速?
} else {
stillTime = 0;
}
// disable friction while jumping
if (!grounded) {
playerPhysicsFixture.setFriction(0f);
playerSensorFixture.setFriction(0f);
} else {
if (!Gdx.input.isKeyPressed(Keys.A) && !Gdx.input.isKeyPressed(Keys.D) && stillTime > 0.2) {
playerPhysicsFixture.setFriction(1000f);
playerSensorFixture.setFriction(1000f);
} else {
playerPhysicsFixture.setFriction(0.2f);
playerSensorFixture.setFriction(0.2f);
// playerPhysicsFixture.setFriction(1000f);
// playerSensorFixture.setFriction(1000f);
}
// dampen sudden changes in x/y of a MovingPlatform a little bit, otherwise
// character hops :)
// 如果没有下面代码,移动平台如果突然往下移动player就会跳起来
if (groundedPlatform != null && groundedPlatform instanceof MovingPlatform
&& ((MovingPlatform)groundedPlatform).dist == 0) {// .dist==0意味着是平台转弯的瞬间施加冲力
player.applyLinearImpulse(0, -24, pos.x, pos.y, true);
// player.applyLinearImpulse(0, 24, pos.x, pos.y, true);
}
}
// since Box2D 2.2 we need to reset the friction of any existing contacts
// 没有下面的代码,在移动和旋转平台上都会停在一个位置不动。摩擦力永远是最大所以就不动?
Array<Contact> contacts = world.getContactList();
for (int i = 0; i < world.getContactCount(); i++) {
Contact contact = contacts.get(i);
contact.resetFriction();
}
// apply left impulse, but only if max velocity is not reached yet
if (Gdx.input.isKeyPressed(Keys.A) && vel.x > -MAX_VELOCITY) {
player.applyLinearImpulse(-2f, 0, pos.x, pos.y, true);
}
// apply right impulse, but only if max velocity is not reached yet
/* if (Gdx.input.isKeyPressed(Keys.D) && vel.x < MAX_VELOCITY) {
player.applyLinearImpulse(2f, 0, pos.x, pos.y, true);
}*/
if (Gdx.input.isKeyPressed(Keys.D)) {
player.applyLinearImpulse(2f, 0, pos.x, pos.y, true);
}// 好像没什么改变,和上面的相比。可能最大速度会变快一点?
// 自己加一个下冲力玩玩
if (Gdx.input.isKeyPressed(Keys.S)) {
player.applyLinearImpulse(0, -40f, pos.x, pos.y, true);
}
// jump, but only when grounded
if (jump) {
jump = false;
if (grounded) {
player.setLinearVelocity(vel.x, 0);
System.out.println("jump before: " + player.getLinearVelocity());
player.setTransform(pos.x, pos.y + 0.01f, 0);
player.applyLinearImpulse(0, 40, pos.x, pos.y, true);
System.out.println("jump, " + player.getLinearVelocity());
}
// 试试多段跳
/* player.setLinearVelocity(vel.x, 0);
System.out.println("jump before: " + player.getLinearVelocity());
//player.setTransform(pos.x, pos.y + 0.01f, 0);// 这里是什么意思?
//player.setTransform(pos.x, pos.y + 5f, 0);
player.applyLinearImpulse(0, 40, pos.x, pos.y, true);
System.out.println("jump, " + player.getLinearVelocity());*/
}
// update platforms
/* for (int i = 0; i < platforms.size; i++) {
Platform platform = platforms.get(i);
platform.update(Math.max(1 / 30.0f, Gdx.graphics.getDeltaTime()));
}*/
// le step...
/**
* timeStep,时步。通常建议设置为1/60,这样的话你的fps最好也设置到60。但是我发现更好的办法就是把timeStep设置为fps的倒数即可。如果你的fps是30,那么就设置为1/30。
* velocityIterations,速度迭代
* positionIterations,位置迭代
* 这两个值通常建议设置为10,更低的迭代值会让你牺牲一些准确性,相反的为你的程序提升一部分性能。
*/
world.step(Gdx.graphics.getDeltaTime(), 4, 4);
// accum += Gdx.graphics.getDeltaTime();
// while(accum > TICK) {
// accum -= TICK;
// world.step(TICK, 4, 4);
// }
player.setAwake(true);
cam.project(point.set(pos.x, pos.y, 0));//把米转换成相对于屏幕的位置
batch.begin();
font.draw(batch, "friction: " + playerPhysicsFixture.getFriction() + "\ngrounded: " + grounded, point.x + 20, point.y);
// 我自己加的,显示位置
font.draw(batch, "position x: "+pos.x+"\nposition y: "+pos.y, point.x +20, point.y - 35);
if(grounded){
}
cam.project(point.set(-50.2f, 2.2f, 0));
// 自己加的,显示坐标
font.draw(batch, "x", point.x, point.y);
for(int i=-50;i<=50;i++){
cam.project(point.set(i, 2, 0));
font.draw(batch, "" + i, point.x, point.y);
}
batch.end();
}
private boolean isPlayerGrounded (float deltaTime) {
groundedPlatform = null;
Array<Contact> contactList = world.getContactList();
for (int i = 0; i < contactList.size; i++) {
Contact contact = contactList.get(i);
if (contact.isTouching()
&& (contact.getFixtureA() == playerSensorFixture || contact.getFixtureB() == playerSensorFixture)) {
Vector2 pos = player.getPosition();
WorldManifold manifold = contact.getWorldManifold();
boolean below = true;
for (int j = 0; j < manifold.getNumberOfContactPoints(); j++) {
/**
* “&=”是JavaScript赋值运算符,意思是将左边变量与右操作数的值按位与。如,a&=b,相当于a=a&b。等号“=”就是赋值用的。
* 而&是JavaScript的位运算符,是按位与的意思,就是当两个操作数的相应位都为1时,该位的结果为1,否则为0。例如,5&6等于4,
* 因为0101&0110(注意都是用二进制表示的)的运算结果是0100。
*
* below =1 判断=1 新的below就是1 在下方?
* below =0 新的below就是0
* 判断=0 新的below就是0
*/
below &= (manifold.getPoints()[j].y < pos.y - 1.5f);
//below &= (manifold.getPoints()[j].y < pos.y-2f);// player落地的时候的接触点y数值大于pos.y,这个时候永远不会落地,因此永远跳不起来。
}
if (below) {
if (contact.getFixtureA().getUserData() != null && contact.getFixtureA().getUserData().equals("p")) {
groundedPlatform = (Platform)contact.getFixtureA().getBody().getUserData();
}
if (contact.getFixtureB().getUserData() != null && contact.getFixtureB().getUserData().equals("p")) {
groundedPlatform = (Platform)contact.getFixtureB().getBody().getUserData();
}
return true;
}
return false;
}
}
return false;
}
@Override
public boolean keyDown (int keycode) {
if (keycode == Keys.W) jump = true;
return false;
}
@Override
public boolean keyUp (int keycode) {
if (keycode == Keys.W) jump = false;
return false;
}
Vector2 last = null;
Vector3 point = new Vector3();
@Override
public boolean touchDown (int x, int y, int pointerId, int button) {
cam.unproject(point.set(x, y, 0));// 将屏幕坐标转换成世界坐标?
if (button == Input.Buttons.LEFT) {
if (last == null) {
last = new Vector2(point.x, point.y);
} else {
createEdge(BodyType.StaticBody, last.x, last.y, point.x, point.y, 0);// 为何手机上的touchdown也是鼠标左键?
last.set(point.x, point.y);
}
} else {
last = null;
}
return false;
}
// 相当于一个接口
abstract class Platform {
abstract void update (float deltatime);
}
class CirclePlatform extends Platform {
Body platform;
public CirclePlatform (int x, int y, float radius, float da) {
// A kinematic body is an hybrid body which is not affected by forces and collisions like a static body but can moved with a linear velocity like a dynamic body.
platform = createCircle(BodyType.KinematicBody, radius, 1);
platform.setTransform(x, y, 0);
platform.getFixtureList().get(0).setUserData("p");
platform.setAngularVelocity(da);
platform.setUserData(this);
}
@Override
void update (float deltatime) {
}
}
class MovingPlatform extends Platform {
Body platform;
Vector2 pos = new Vector2();
Vector2 dir = new Vector2();
float dist = 0;
float maxDist = 0;
/**
* da: angle?
* dx: move x?
* dy: move y? 数值越大移动速度越快,dx,dy数值不同还改变角度
* maxDist: max distance to move
*/
public MovingPlatform (float x, float y, float width, float height, float dx, float dy, float da, float maxDist) {
platform = createBox(BodyType.KinematicBody, width, height, 1);// density 不知道代表什么,还需要看box2d的教程才知道。
pos.x = x;
pos.y = y;
dir.x = dx;
dir.y = dy;
this.maxDist = maxDist;
platform.setTransform(pos, 0);
platform.getFixtureList().get(0).setUserData("p");
//platform.getFixtureList().get(0).setUserData("q");
platform.setAngularVelocity(da);
platform.setUserData(this);
}
public void update (float deltaTime) {
dist += dir.len() * deltaTime;
if (dist > maxDist) {
dir.scl(-1);
dist = 0;
}
platform.setLinearVelocity(dir);// 以dxdy为基础的速度方向
}
}
}
[LIBGDX学习]LibGDX代码详解(三)Box2D character control
最新推荐文章于 2021-05-30 12:39:09 发布