Alternative to using get() in a big PGraphics

Hey so I am trying to write a simple space fighting game that can be played on LAN I am using PGraphics to generate a map that has a size of 10000x10000 (I know a bit overkill but I also tried 5000x5000 and increasing did not have much effect on fps) and since this game is in 2D I did not use camera() but I used the get() function on PGraphics to get a part of the map that the player is currently at.

I am getting about 60 FPS with JAVA2D but I see dips that go under 20 FPS which is pretty noticable to the eye and makes the experience worse.

So I tried P2D and got a FPS of about 5 (later I discovered P2D does NOT like pixel manipulation so I stopped using it)

Then I tried FX2D wow it was much better the quality the FPS everything was amazing until I noticed the CPU usage It was around 65%!! so I went back to using JAVA2D

The culprit for these dips in FPS is because I am using get() on PGraphics to get a part of it I also tried just showing the whole PGraphics on screen with image() and well lets just say it did not go well (FPS under 5)

So my question is is there an alternative to get() for this purpose?

Here is my system specs (not the best laptop :smiley: ):
Intel Core i5 3230M
8GB of ram
using windows 8.1 pro
processing version 3.4

Here is my code:

Space.pde:

import oscP5.*;
import netP5.*;
import processing.net.*;
OscP5 oscP5;
Map map;
Player pl;
Indicator life;
Indicator turbo;
int myListeningPort = 12000;
int myBroadcastPort = 32000;
NetAddress myBroadcastLocation;
Laser laser;
int seed;
String myConnectPattern = "/server/connect";
String myDisconnectPattern = "/server/disconnect";
PImage ship;
String ip;
boolean tired = false;

void setup() {
  fullScreen();
  cursor(CROSS);
  ship = loadImage("ship.png");
  ship.resize(60, 60);
  pl = new Player();
  laser = new Laser();
  life = new Indicator("Life", 100, width - 265, height - 65, 100);
  turbo = new Indicator("Turbo", 100, 15, height - 65, 100);
  map = new Map();
  oscP5 = new OscP5(this, myListeningPort);
  myBroadcastLocation = new NetAddress("127.0.0.1", myBroadcastPort);
  OscMessage m = new OscMessage("/server/connect", new Object[0]);
  oscP5.flush(m, myBroadcastLocation);
  ip = Server.ip();
  textAlign(CORNER);
  textSize(36);
}

void draw() {
  if (frameCount < 10) surface.setLocation((displayWidth / 2) - width / 2, (displayHeight / 2) - height / 2);
  background(255);
  image(map.getMap(), 0, 0);
  life.show();
  turbo.show();
  if (life.getVal() <= 0) {
    pl.killed();
    life.setVal(100);
  }
  if (turbo.getVal() >= 100) tired = false; 
  if (keyPressed && (key == 'q' || key == 'Q') && turbo.getVal() >= 0 && !tired && dist(mouseX, mouseY, width / 2, height / 2) > 70) {
    turbo.setVal(turbo.getVal() - 1);
    pl.mag = 30;
    if (turbo.getVal() <= 0) tired = true;
  } else {
    pl.mag = 5;
    if (turbo.getVal() < 100) turbo.setVal(turbo.getVal() + .2);
  }
  text(round(frameRate), 0, 30);
  translate(width / 2, height / 2);
  laser.move();
  laser.show();
  rotate(new PVector(mouseX - width / 2, mouseY - height / 2).heading());
  imageMode(CENTER);
  image(ship, 0, 0);
  translate(0, 0);
  imageMode(CORNER);
  pl.move();
  if (mousePressed && mouseButton == LEFT  && dist(mouseX, mouseY, width / 2, height / 2) > 5) {
    PVector l = new PVector(pl.loc.x, pl.loc.y);
    laser.fire(l, new PVector(mouseX - width / 2, mouseY - height / 2).setMag(30));
  }
}

void oscEvent(OscMessage message) {
  if (message.addrPattern().equals("map")) {
    seed = message.get(0).intValue();
    randomSeed(seed);
    map = new Map();
  }
}

Indicator.pde:

class Indicator {
  String name;
  float max;
  float x;
  float y;
  float val;
  
  Indicator(String name, float max, float x, float y, float startval) {
    this.name = name;
    this.max = max;
    this.x = x;
    this.y = y;
    val = startval;
    rectMode(CORNER);
  }
  
  void show() {
    fill(100);
    rect(x, y, 250, 50);
    fill(map(val, 0, max, 255, 0), map(val, 0, max, 0, 255), 0);
    rect(x, y, map(val, 0, max, 0, 250), 50);
    textAlign(CENTER);
    fill(0);
    text(name, x + 125, y + 35);
    fill(255);
    textAlign(CORNER);
  }
  
  void setVal(float setval) {
    val = setval;
  }
  
  float getVal() {
    return val;
  }
}

Laser.pde (come on what is a space game without lasers?):

class Laser {
  PVector loc;
  PVector sloc;
  PVector locos;
  PVector vel;
  boolean active = false;

  Laser() {
  }

  void show() {
    if (active) ellipse(locos.x, locos.y, 10, 10);
  }

  void move() {
    if (active) {
      loc.add(vel);
      locos.add(vel);
      OscMessage locmes = new OscMessage("laserloc");
      locmes.add(ip);
      locmes.add(loc.x);
      locmes.add(loc.y);
      locmes.add(loc.heading());
      oscP5.send(locmes, myBroadcastLocation);
      if (dist(sloc.x, sloc.y, loc.x, loc.y) > 1000) active = false;
    }
  }

  void fire(PVector loc, PVector vel) {
    if (!active) {
      active = true;
      this.loc = new PVector(loc.x, loc.y);
      this.vel = vel;
      sloc = new PVector(loc.x, loc.y);
      locos = new PVector(0, 0);
    }
  }
}

Map.pde (the part I really need help is getMap()):

class Map {
  
  PGraphics map;
  
  Map() {
    map = createGraphics(10000, 10000);
    map.beginDraw();
    map.imageMode(CENTER);
    map.background(0);
    map.stroke(255);
    for (int i = 0; i < 10000; i++) {
      for (int q = 0; q < 10000; q++) {
        map.strokeWeight(random(3));
        if (random(100) > 99.9) map.point(i, q);
      }
    }
    map.endDraw();
  }
  
  PImage getMap() {
    return map.get(int(pl.loc.x) - width / 2, int(pl.loc.y) - height / 2, width, height);
  }
}

Player.pde:

class Player {

  PVector loc;
  float mag = 5;

  Player() {
    loc = new PVector(random(5000), random(5000));
  }


  void move() {
    if (dist(mouseX, mouseY, width / 2, height / 2) > 70) {
      loc.add(new PVector(mouseX - width / 2, mouseY - height / 2).setMag(mag));
      OscMessage locmes = new OscMessage("loc");
      locmes.add(ip);
      locmes.add(loc.x);
      locmes.add(loc.y);
      locmes.add(loc.heading());
      oscP5.send(locmes, myBroadcastLocation);
    }
  }
  
  void killed() {
    loc = new PVector(random(5000), random(5000));
  }
}

I am open to any opinions on the game optimizations on the code (it really is messy and unnecessarilly long) (by the way you dont need to worry about the LAN thing since I am just testing just hit the play button oscP5 does not do anything yet :smiley: )

Oh almost forgot here is ship.png:

ship

Use the 9 argument version of image() to draw the PGraphics to screen directly.

Consider splitting your map into tiles maybe 2048x2048 size.

Sorry for wasting your time but what is the syntax for the 9 argument image function?

thanks for the suggestion btw :smiley:

http://processing.github.io/processing-javadocs/core/processing/core/PGraphics.html#image-processing.core.PImage-float-float-float-float-int-int-int-int-

First 4 floats are the rectangle that you want to draw the image (or part of it) into, as x, y, width and height. The 4 ints are the coordinates of the two points in the source image that define the region. Note that the last two arguments are the absolute coordinates not the width and height of the source region.

eg. to draw a region 200,150 of your map with top left 400,300 at 100,100 on screen you would use

image(map, 100, 100, 200, 150, 400, 300, 600, 450);

For some reason this isn’t on the image() page at image() / Reference / Processing.org Seems to be an odd omission.

4 Likes

Just tried your suggestion but there is no improvement

thanks anyway

Oh sorry i did it wrong IT WORKS

Thanks so much

1 Like