/** Constructor: Sets up this panel and loads the images. */ public Engine() { addKeyListener(new Controller()); setBackground(Color.black); setPreferredSize(new Dimension(WIDTH, HEIGHT)); setFocusable(true); // init world final int defWidth = 150; final int defHeight = 15; final Random gen = new Random(); // 50% random, 25% hills, 25% platform if (gen.nextBoolean()) { world = RandomLevel.genWorldRandom(defWidth, defHeight, tp); } else { if (gen.nextBoolean()) { world = RandomLevel.genWorldHills(defWidth, defHeight, tp); } else { world = RandomLevel.genWorldPlatform(defWidth, defHeight, tp); } } // world = new Level("house.txt", tp); // init hero hero = new Hero(); hero.setImage(tp.get(Texture.hero)); hero.setPos(world.getStart()); renderer.addDynProp(hero); renderer.setWorld(world); // init test light Point[] bgLights = world.getAll(Texture.bgLightDead); Light tempLight; final int cso = 15; // Cell size offset. final int rRange = 100; final int rMin = 75; for (Point light : bgLights) { tempLight = new Light(light.x + cso, light.y + cso, world); tempLight.setRadius((short) (Math.random() * rRange + rMin)); renderer.addStaticProp(tempLight); triggers.add(tempLight); } // Don't start this loop until setup is complete! if (thread == null) { running = true; thread = new Thread(this); thread.start(); } }
/** * Compare an object to the world to identify any collisions. The obj is compared to blocks it is * directly overtop of. Returns null if no collisions are found. * * @param obj The entity to test against the world. * @param target The block type to test for * @return the first collision found, if any. Null if none. */ private Point getWorldCollision(Dynamic obj, Texture target) { Rectangle r = obj.getCollisionBox(); Point lowerLeft = new Point(r.x, r.y); Point upperRight = new Point(r.x + r.width - 1, r.y + r.height - 1); int cell = World.CELL_SIZE; // If x is negative, offset by 1. It sucks but is necessary. if (lowerLeft.x < 0) { lowerLeft.x -= cell; } if (upperRight.x < 0) { upperRight.x -= cell; } // Convert pixel coordinates to world coordinates. lowerLeft.x /= cell; lowerLeft.y /= cell; upperRight.x /= cell; upperRight.y /= cell; // Test for overlap with the target block type for (int x = (lowerLeft.x); x <= (upperRight.x); x++) { for (int y = (lowerLeft.y); y <= (upperRight.y); y++) { if (world.getCell(x, y) == target) { return new Point(x, y); } } } return null; }
/** * Calculate the physics in our world for the given object. * * @param obj The object to move * @param timeStep The time since last evaluation in nanoseconds */ public void simulate(Dynamic obj, long timeStep) { /* Actions: * 1. apply drag * 2. add forces to object * - gravity (unless resting on block) * - arrow keys * * 3. apply forces to object. (done by the object) * * 4. a) move in x axis * 5. a) resolve collisions in x axis * * 4. b) move in y axis * 5. b) resolve collisions in y axis */ double seconds = timeStep / (double) NS_PER_S; // 1. apply drag obj.applyDrag(seconds); // 2. add forces obj.addForce(new Vector2D(0, GRAVITY)); obj.addForce(keyForce); // 3. apply forces obj.applyForces(seconds); // get velocity in preparation for step 4 Vector2D vel = obj.getVel(); hero.setImage(tp.get(Texture.hero)); // 4. a) move x obj.move((int) (vel.x * seconds), 0); // 5. a) resolve collisions in x Point bad = getWorldCollision(obj, Texture.brick); // bad = null; if (bad != null) { int resolution = world.escapeX(obj, vel.x * seconds, bad); obj.move(resolution, 0); obj.getVel().setX(0); } // 4. b) move y obj.move(0, (int) (vel.y * seconds)); // 5. b) resolve collisions in y bad = getWorldCollision(obj, Texture.brick); // bad = null; if (bad != null) { int resolution = world.escapeY(obj, vel.y * seconds, bad); if (vel.y < 0) { hero.setOnGround(true); hero.setImage(tp.get(Texture.heroGround)); } obj.move(0, resolution); obj.getVel().setY(0); } else { hero.setOnGround(false); } }