Ptmx: How to draw overlayed maps (for vertical scrolling platform game)

edited January 2018 in Library Questions

This is my first question posting to the forum, though I have been working with Processing for a couple of years. I am now in the process of helping my son to learn object-oriented programming and Processing is the ideal starting point for him.

We have been working with "Tiled" Map Editor mapeditor.org for a simple vertical scrolling platform game. Below is an example of a tile map that my son created which has two tile layers "Background" and "Foreground" (the objects are not being used in the sketch at present):

https://www.flickr.com/photos/njl-photolog/27824867269

Unfortunately, when we try to draw the "Background" layer with the "Foreground" layer overlayed on top we just get the "Foreground" layer. It appears that empty tiles and transparent areas of other tiles are being painted as black which paints over the "Background" layer tiles:

https://www.flickr.com/photos/njl-photolog/39572556302

What we want to see happen is the "Foreground" layer overlay the "Background" and preserve the transparency. This way we can either move the "Background" together for the "Foreground" layer or at a different speed to create a parallax effect.

The code we are using is a modified version of the "MovingFree" example from the Ptmx library. I haven't posted the additional files (TMX, TSX and PNGs) needed to run this sketch -- it would make a very long post, but I will email a zip file of the whole thing if necessary to anyone.

The sketch code we are using is here:

// Example of using "Tiled" generated maps
// Modified from the PTMX library's example "MovingFree"

import ptmx.*;

final int tileSize = 70;

Ptmx map;
PImage smiley;
int xMapOffset = 7 * tileSize;
int xMapPosition, yMapPosition;
int xMovement = tileSize;

boolean flagLeft, flagRight, flagUp, flagDown; // Arrow direction keys that are being pressed

// Layer IDs are hard-wired for now.
// Note: It would be great to have an accessor method, like getName(i), to retrieve a layer's text name ;-)
int idForegroundLayer = 1;
int idBackgroundLayer = 0;

void setup() {
  // Long way round to set window size using variables. ;-)
  int dw = 15 * tileSize;
  int dh = 10 * tileSize;
  size(600, 400);
  frameRate(10);
  surface.setResizable(true);
  surface.setSize(dw, dh);

  smiley = loadImage("assets/smiley.png");

  map = new Ptmx(this, "Level_Two.tmx");
  map.setDrawMode(CORNERS);
  map.setPositionMode("CANVAS"); // Default Position Mode

  // Starting position for a vertical scrolling platform
  xMapPosition = 0;
  yMapPosition = 0;
  imageMode(CORNERS); // x,y refer to the upper-left corner
}

void draw() {
  int xSprite = width / 2;
  int ySprite = 8 * tileSize;
  String textDir = "";
  int tileNum;

  // In "Level_Two.tmx" the Map Properties->Background Color is set to 120,219,231 "Shade of cyan",
  // but empty tiles are painted as black squares so no cyan background appears
  background(map.getBackgroundColor());

  // Tiles in the foreground layer paint (with black) over tiles in the background!
  map.draw(idBackgroundLayer, xMapPosition, yMapPosition);
  map.draw(idForegroundLayer, xMapPosition, yMapPosition);

  image(smiley, xSprite, ySprite); // Draw player's sprite image

  int prevX = xMapPosition;
  int prevY = yMapPosition;

  if (flagLeft)  { xMapPosition -= xMovement; textDir += "<"; }
  if (flagRight) { xMapPosition += xMovement; textDir += ">"; }

  // Retrieve tile number that we are over in the foreground layer
  tileNum = map.getTileIndex(idForegroundLayer, int((xMapPosition + xSprite) / tileSize), int((yMapPosition + ySprite) / tileSize));

  // Show on-screen diagnostic information
  textSize(18);
  fill(200);
  text("Move (almost) free. Map x,y = " + xMapPosition + "," + yMapPosition +
      " Sprite x,y = " + xSprite + "," + ySprite + " tile = " + tileNum + " dir: " + textDir, 10, 50);
}


void keyPressed(){
  if (keyCode == LEFT) flagLeft = true;
  if (keyCode == RIGHT) flagRight = true;
}

void keyReleased(){
  if (keyCode == LEFT) flagLeft = false;
  if (keyCode == RIGHT) flagRight = false;
}

FYI: I have contacted the author of the Ptmx library (linux-man) prior to posting this question, but I wanted to post our question and examples on the forum so other to could see it too.

Tagged:

Answers

  • edited January 2018

    I apologize I tried using the "C" icon and Ctrl-O (on a Mac) and unfortunately I can not get the sketch to be formatted as code. Again, this is first post so go easy on me. ^:)^

    Edit: OK, I finally figured it!

  • Answer ✓

    For a "normal" use of a Tiled map, you can use the simple draw method and control each layer with the visible property. Instead of

    map.draw(idBackgroundLayer, xMapPosition, yMapPosition);
    map.draw(idForegroundLayer, xMapPosition, yMapPosition);
    

    you just use

    map.draw(xMapPosition, yMapPosition);
    

    but for animated backgrounds and parallax, things get tricky. Each map.draw to Canvas clear the background (I remember it happens because I used the same code to draw on Canvas and PGraphics, and Canvas don't accept transparency), so if you want to "mix" Layers and other images, you should draw them to a PGraphics (where each layer is drawn on a transparent background).

    PGraphics pgBackground, pgForeground;
    ...
    void setup() {
    pgBackground = createGraphics(width, height);
    pgForeground = createGraphics(width, height);
    ...    
    void draw() {
    ...
    map.draw(pgBackground, idBackgroundLayer, xMapPosition, yMapPosition);
    map.draw(pgForeground, idForegroundLayer, xMapPosition, yMapPosition);
    image(pgBackground, 0, 0);
    image(pgForeground, 0, 0);
    

    It probably could be easier :-?

    Take a look at the "Layers" example.

  • Wow! That is a really subtle detail about Canvas not accepting transparency. Looking at the "Layers" example now makes a lot more sense.

    I have implemented the changes you suggested and the two layers are overlaid correctly so that the tiles in the "Background" show through.

    My son and I can now get on with developing a simple game. Thank you. :-bd

  • Subtle but logical. Like on Photoshop or Gimp, the base layer can't be transparent.

    There's another way:

    Drop sketchbook/libraries/ptmx/src/ptmx/Ptmx.java on Processing or use Sketch->Add File...

    Save. You will find the java file on sketch folder. This way you are using the library directly from the java source.

    On pde:

    Comment import ptmx.*;

    On Ptmx.java:

    Comment the first line package ptmx;

    Comment line 406 pg.clear(); It's this line that clears the background on each draw.

    Now you can use your original code. Also, you must now explicitly clear the canvas or PGraphics before each draw if you want to.

  • That comparison with Photoshop helps--I've spent many years working with it for digital photography.

    I admit to being ignorant to the subtle details of Processing (and Java's) graphics. I have used Processing more for data processing and communications with Arduinos. :-B

    Thanks for the suggestion about bringing the Ptmx library into the sketch, as a new tab. I confess I had tried that and had looked at whether I should comment out line 406. Though, I did not do that as I read the statement: "This function clears everything in a PGraphics object to make all of the pixels 100% transparent." (emphasis added) (https://processing.org/reference/clear_.html).

    It must have been late at night and I was fatigued because I fixated on to the last part of the statement. 8-|

    But, you have answered my question and the problem is resolved. Thank you.

  • One last note:

    That's right. It clears a PGraphic, but since there is no transparency on Canvas, it just blacks all pixels.

    I felt inspired by your issue and updated the library. Now there is a Background Mode that can be "COLOR", "CLEAR" and "NONE".

    Please look at the github README and the layers example for more info.

  • Cool! Thank you.

    One item that would help us, if you have time, is adding new method that returns the text name of the Tiled layer, called for example "getLayerName()".

    My son and I were inspired by a short video on creating a game level (using Tiled) for "Commander Cool": https://youtube.com/watch?v=2_KB4tOTH6w--duration is 1min 47 secs.

  • Great! Thank you.

    I fret my next question is going to create more work, but shouldn't Processing's Library Contribution Manager being showing the Ptmx library as having an update?

  • edited January 2018 Answer ✓

    It should. Maybe we're moving too fast for the update script processing.org is using... Try an uninstall/install.

  • Yes, we were moving to fast for the system. The uninstall and re-install trick worked. Thank you again.

Sign In or Register to comment.