Perpetuum mobile

    Good afternoon, dear user habr.com! This is the third article on the topic. I work all day long, I can not tear myself away from the amazing Box2D library.

    If you have not read the first and second articles, be sure to look, it will be fun! I work in Eclipse , I write in Java. Why did I call my article so? Read on - and very soon everything will become clear! Spoiler: we will make our own perpetual motion machine (including for cars), and, perhaps, we will create the machine itself!

    image

    Figure 1. Perpetual motion machine.

    So, today we will try to get something like this:

    image

    Figure 2. A machine with an engine.

    Yes, this is not a typo! Today we will make a machine with a real engine, it will be indistinguishable from the real one! This is not the “cart” for you from the first article.

    For connecting libGDX, see the first article.

    Here is a picture showing how my assembly looks. I added the Utils package with the Constants class to the Core folder, which contains only one constant - the number of pixels per meter. This is so that the world is not gigantic.

    image

    Figure 3. My build.

    Here is the code for the DesktopLauncher class from com.mygdx.game.desktop:

    Paste this code into the class and forget about it.
    package com.mygdx.game.desktop;
    import com.badlogic.gdx.backends.lwjgl.LwjglApplication;
    import com.badlogic.gdx.backends.lwjgl.LwjglApplicationConfiguration;
    import com.mygdx.game.MyGdxGame;
    public class DesktopLauncher {
    	public static void main(String[] arg) {
    		LwjglApplicationConfiguration config = new LwjglApplicationConfiguration();
    		// ширина окна
    		config.width = 720;
    		// высота окна
    		config.height = 480;
    		config.backgroundFPS = 60;
    		config.foregroundFPS = 60;
    		new LwjglApplication(new MyGdxGame(), config);
    	}
    }
    


    The following code for the MyGdxGame class is from the com.mygdx.game package. Everything is in the code comments.

    We create the world and the machine.
    
    package com.mygdx.game;
    import com.badlogic.gdx.ApplicationAdapter;
    import com.badlogic.gdx.Gdx;
    import com.badlogic.gdx.Input.Keys;
    import com.badlogic.gdx.graphics.GL20;
    import com.badlogic.gdx.graphics.OrthographicCamera;
    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.Box2DDebugRenderer;
    import com.badlogic.gdx.physics.box2d.CircleShape;
    import com.badlogic.gdx.physics.box2d.FixtureDef;
    import com.badlogic.gdx.physics.box2d.PolygonShape;
    import com.badlogic.gdx.physics.box2d.World;
    import com.badlogic.gdx.physics.box2d.joints.RevoluteJointDef;
    import utils.Constants;
    public class MyGdxGame extends ApplicationAdapter {
    	private OrthographicCamera camera;
    	private boolean DEBUG = false;
    	private World world;
    	private Box2DDebugRenderer b2dr;
    	// кузов
    	private Body box;
    	// заднее колесо
    	private Body backweel;
    	// переднее колесо
    	private Body frontweel;
    	private Body floor;
    	private Body triangle;
    	// скорость мотора
    	private float m = -20;
    	public void create() {
    		float w = Gdx.graphics.getWidth();
    		float h = Gdx.graphics.getHeight();
    		camera = new OrthographicCamera();
    		camera.setToOrtho(false, w / 2, h / 2);
    		world = new World(new Vector2(0, -9.8f), false);
    		b2dr = new Box2DDebugRenderer();
    		// создаем землю
    		floor = createFloor();
    		// создаем препятствия
    		triangle = createTriangle(20f, 40f, 30f, -2f, -2f, 5f);
    		triangle = createTriangle(50f, 60f, 80f, -2f, 10f, -2f);
    		triangle = createTriangle(100f, 160f, 200f, -2f, 20f, -2f);
    		triangle = createTriangle(280f, 290f, 300f, -2f, 100f, -2f);
    		// создаем кузов
    		box = createBox();
    		// создаем колеса
    		backweel = createWeel(-1f, 2f);
    		frontweel = createWeel(4.5f, 2f);
    		// здесь создаются связи между кузовом и колесами
    		connected();
    	}
    	// связи между кузовом и колесами
    	public void connected() {
    		revJoint(box, backweel, -1f * 10 / utils.Constants.PPM, -2f * 10 / utils.Constants.PPM);
    		revJoint(box, frontweel, 4.5f * 10 / utils.Constants.PPM, -2f * 10 / utils.Constants.PPM);
    	}
    	// здесь самое интересное! Читайте внимательнее!
    	public void revJoint(Body body1, Body body2, float xo, float yo) {
    		// настройка связи
    		RevoluteJointDef rjd = new RevoluteJointDef();
    		// здесь настраивается мотор
    		rjd.enableMotor = true;
    		// скорость мотора. Если обратите внимание на дальнейший код, станет ясно, что
    		// это константа. Мотор никогда не остановится. Вот он - наш вечный двигатель!
    		rjd.motorSpeed = m;
    		// лошадиные силы
    		rjd.maxMotorTorque = 30;
    		rjd.collideConnected = false;
    		rjd.bodyA = body1;
    		rjd.bodyB = body2;
    		rjd.localAnchorA.set(xo, yo);
    		world.createJoint(rjd);
    	}
    	public void render() {
    		update(Gdx.graphics.getDeltaTime());
    		Gdx.gl.glClearColor(0, 0, 0, 1);
    		Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
    		b2dr.render(world, camera.combined.scl(Constants.PPM));
    	}
    	public void resize(int width, int height) {
    		camera.setToOrtho(false, width / 2, height / 2);
    	}
    	public void dispose() {
    		world.dispose();
    		b2dr.dispose();
    	}
    	public void update(float delta) {
    		world.step(1 / 60f, 6, 2);
    		cameraUpdate(delta);
    		inputUpdate(delta);
    	}
    	// по нажатию на пробел машина получает импульс, который толкает ее вперед
    	public void inputUpdate(float delta) {
    		if (Gdx.input.isKeyPressed(Keys.SPACE)) {
    			box.setLinearVelocity(5, 5);
    		}
    	}
    	// камера следует за кузовом
    	public void cameraUpdate(float delta) {
    		Vector3 position = camera.position;
    		position.x = box.getPosition().x * Constants.PPM;
    		position.y = box.getPosition().y * Constants.PPM;
    		camera.position.set(position);
    		camera.update();
    	}
    	// создаем кузов сложной формы
    	public Body createBox() {
    		Vector2[] verticles = new Vector2[8];
    		verticles[0] = new Vector2(2f * 10 / utils.Constants.PPM, 2f * 10 / utils.Constants.PPM);
    		verticles[1] = new Vector2(2f * 10 / utils.Constants.PPM, -2f * 10 / utils.Constants.PPM);
    		verticles[2] = new Vector2(-2f * 10 / utils.Constants.PPM, 2f * 10 / utils.Constants.PPM);
    		verticles[3] = new Vector2(-2f * 10 / utils.Constants.PPM, -2f * 10 / utils.Constants.PPM);
    		verticles[4] = new Vector2(6f * 10 / utils.Constants.PPM, -2f * 10 / utils.Constants.PPM);
    		verticles[5] = new Vector2(6f * 10 / utils.Constants.PPM, 0f * 10 / utils.Constants.PPM);
    		verticles[6] = new Vector2(-1f * 10 / utils.Constants.PPM, -2f * 10 / utils.Constants.PPM);
    		verticles[7] = new Vector2(4.5f * 10 / utils.Constants.PPM, -2f * 10 / utils.Constants.PPM);
    		PolygonShape shape = new PolygonShape();
    		Body fBody;
    		BodyDef def = new BodyDef();
    		def.type = BodyDef.BodyType.DynamicBody;
    		def.position.set(0, 0);
    		def.fixedRotation = false;
    		fBody = world.createBody(def);
    		shape.set(verticles);
    		// его физические параметры
    		FixtureDef fd = new FixtureDef();
    		// форма, как полигон из точек
    		fd.shape = shape;
    		// коэффициент трения
    		fd.friction = 0.5f;
    		// коэффициент плотности
    		fd.density = 3f;
    		// коэффициент упругости
    		fd.restitution = 0.5f;
    		fBody.createFixture(fd);
    		fBody.createFixture(fd);
    		shape.dispose();
    		return fBody;
    	}
    	// создаем колеса
    	public Body createWeel(float xo, float yo) {
    		CircleShape shape = new CircleShape();
    		Body fBody;
    		BodyDef def = new BodyDef();
    		def.type = BodyDef.BodyType.DynamicBody;
    		def.position.set(xo * 10 / utils.Constants.PPM, yo * 10 / utils.Constants.PPM);
    		def.fixedRotation = false;
    		fBody = world.createBody(def);
    		shape.setRadius(2.0f * 10 / utils.Constants.PPM);
    		// его физические параметры
    		FixtureDef fd = new FixtureDef();
    		fd.shape = shape;
    		fd.friction = 0.7f;
    		fd.density = 3f;
    		fd.restitution = 0.5f;
    		fBody.createFixture(fd);
    		shape.dispose();
    		return fBody;
    	}
    	// создаем пол
    	public Body createFloor() {
    		Vector2[] verticles = new Vector2[4];
    		verticles[0] = new Vector2(-100f * 10 / utils.Constants.PPM, -2f * 10 / utils.Constants.PPM);
    		verticles[1] = new Vector2(300f * 10 / utils.Constants.PPM, -2f * 10 / utils.Constants.PPM);
    		verticles[2] = new Vector2(300f * 10 / utils.Constants.PPM, -5f * 10 / utils.Constants.PPM);
    		verticles[3] = new Vector2(-100f * 10 / utils.Constants.PPM, -5f * 10 / utils.Constants.PPM);
    		PolygonShape shape = new PolygonShape();
    		Body fBody;
    		BodyDef def = new BodyDef();
    		def.type = BodyDef.BodyType.StaticBody;
    		def.position.set(0, 0);
    		def.fixedRotation = true;
    		fBody = world.createBody(def);
    		shape.set(verticles);
    		FixtureDef fd = new FixtureDef();
    		fd.shape = shape;
    		fd.friction = 0.5f;
    		fd.density = 3f;
    		fd.restitution = 0.5f;
    		fBody.createFixture(fd);
    		shape.dispose();
    		return fBody;
    	}
    	// да здравствует полоса препятствий!
    	public Body createTriangle(float xo, float x1, float x2, float yo, float y1, float y2) {
    		Vector2[] verticles = new Vector2[3];
    		verticles[0] = new Vector2(xo * 10 / utils.Constants.PPM, yo * 10 / utils.Constants.PPM);
    		verticles[1] = new Vector2(x1 * 10 / utils.Constants.PPM, y1 * 10 / utils.Constants.PPM);
    		verticles[2] = new Vector2(x2 * 10 / utils.Constants.PPM, y2 * 10 / utils.Constants.PPM);
    		PolygonShape shape = new PolygonShape();
    		Body fBody;
    		BodyDef def = new BodyDef();
    		def.type = BodyDef.BodyType.StaticBody;
    		def.position.set(0, 0);
    		def.fixedRotation = true;
    		fBody = world.createBody(def);
    		shape.set(verticles);
    		FixtureDef fd = new FixtureDef();
    		fd.shape = shape;
    		fd.friction = 0.5f;
    		fd.density = 10f;
    		fd.restitution = 0f;
    		fBody.createFixture(fd);
    		shape.dispose();
    		return fBody;
    	}
    }
    



    image
    Figure 4. What do we get when compiling?

    We create our car a motor that spins its wheels. But it works forever, because there are no energy losses. Thus we create our perpetual motion machine!

    And you wonder what is at the end of the level? Send screenshots in the comments as you go. Thank you for reading the article to the end! I hope you can create your own car in Box2D!

    PS I look forward to your questions in the comments! Long live the gaming physics!

    Also popular now: