/** 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(); } }
/** * Draws everything onto the main panel. * * @param page Graphics component to draw on */ public void paintComponent(Graphics page) { super.paintComponent(page); setForeground(Color.cyan); renderer.draw(page, this, offX, offY); // hero.draw(this, page, offX, offY); for (Entity effect : effects) { effect.draw(this, page, offX, offY); /*if (effect instanceof Burst) { Burst pop = (Burst) effect; pop.draw(this, page, offX, offY); }*/ } // Draw hero position information Rectangle r = hero.getBounds(); page.drawString("Position: x = " + (r.x + (r.width >> 1)), SCORE_PLACE_X, SCORE_PLACE_Y); final int tab = 100; final int lineHeight = 20; page.drawString("y = " + (r.y + (r.height >> 1)), SCORE_PLACE_X + tab, SCORE_PLACE_Y); page.drawString("Frame: " + frame, SCORE_PLACE_X, SCORE_PLACE_Y + lineHeight); if (!running) { page.drawString("Game over :(", WIDTH / 2, HEIGHT / 2); } }
/** The main loop, that controls all the action. It repeats until game over. */ public void run() { long start = System.nanoTime(); long elapsed; long wait; final int minTimeBetweenFrames = 5; while (running) { // Timing // elapsed is the number of nanoseconds since last frame elapsed = System.nanoTime() - start; elapsed = Math.min(MAX_FRAME, elapsed); start = System.nanoTime(); Point pos = hero.getPos(); frame++; // Physics! simulate(hero, elapsed); // Move camera updateCamPos(); // Test 'victory' conditions if (pos.y < LOWER_BOUND) { running = false; } // Test world events (like touching a light) testTriggers(triggers); // Delete (release) dead effects. deleteDeadEntities(); // redraw everything repaint(); // "elapsed" is reassigned to the number of nanoseconds // since this frame began elapsed = System.nanoTime() - start; // wait is how much time is remaining // before the next frame needs to be drawn. wait = targetTime - elapsed / NS_PER_MS; // wait at least 5 ms between frames. if (wait < 0) { wait = minTimeBetweenFrames; } try { Thread.sleep(wait); } catch (InterruptedException e) { e.printStackTrace(); } } // end while } // end run
/** * Tests if the hero triggers world events, like by touching a light. * * @param trigs A list of triggers to test. */ private void testTriggers(ArrayList<Trigger> trigs) { for (Trigger trigger : trigs) { if (trigger.isTriggered(hero.getBounds())) { trigger.triggerAction(); Entity burst = new Burst(tp); burst.setPos(((Light) trigger).getPos()); effects.add(burst); if (trigger instanceof Drawable) { renderer.invalidate(((Drawable) trigger).getBounds()); } } } }
/** Move the camera so that our hero doesn't go off-screen. */ private void updateCamPos() { final int marginX = (int) (getWidth() * 0.25); final int marginTop = (int) (getHeight() * 0.2); final int marginBottom = (int) (getHeight() * 0.2); Point pos = hero.getPos(); Dimension dim = hero.getSize(); // update x motion // If too far left if (pos.x - marginX < offX) { offX = pos.x - marginX; // if too far right } else if (pos.x + marginX > offX + getWidth() - dim.width) { offX = pos.x + marginX - getWidth() + dim.width; } // update y motion // If too low if (pos.y - marginBottom < offY) { offY = pos.y - marginBottom; // If too high } else if (pos.y + marginTop > offY + getHeight() - dim.height) { offY = pos.y + marginTop - getHeight() + dim.height; } }
/** * 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); } }