How to put an array of images inside a function?

So I’m writing a Super Mario/platform game for school, and I’m trying to make a hand-drawn character move around the screen, in three modes:

  1. Idle: just standing, default mode if no key is pressed
  2. Running: when right or left key is pressed
  3. Jumping: when up key is pressed

Idle and jumping is done with absolutely no problems, but I got stuck on the running part. I drew four images for the running character, but it doesn’t work outside of void draw ().

So it works fine like this:

PImage idle;
PImage [] run = new PImage [4];
int currentImageNum;
int NumofImages = 4;
PImage jump;
int x;
int y;

void setup ()
{
  size (1280, 720);
  smooth ();
  //standing
  idle = loadImage ("idle.png");
  //running
  run [0] = loadImage ("run0.png");
  run [1] = loadImage ("run1.png");
  run [2] = loadImage ("run2.png");
  run [3] = loadImage ("run3.png");
  currentImageNum = 0;
  frameRate (12);
  //jumping
  jump = loadImage ("jump.png");
  x = width/2;
  y = height/2;
}

void draw ()
{ 
  background (0);
  image (run [currentImageNum], x, y, 100, 100);
  currentImageNum++;
  if (currentImageNum >= NumofImages)
  currentImageNum = 0;
}

So that works fine. But that’s just the character running and nothing else. So when I try something like this (putting the array inside void run () so I can just reference the function when I need it):

PImage idle;
PImage [] run = new PImage [4];
int currentImageNum;
int NumofImages = 4;
PImage jump;
int x;
int y;
PImage boy; 
boolean load = false;

void setup () {
  size (1280, 720);
  smooth ();
  //standing
  idle = loadImage ("idle.png");
  //running
  run [0] = loadImage ("run0.png");
  run [1] = loadImage ("run1.png");
  run [2] = loadImage ("run2.png");
  run [3] = loadImage ("run3.png");
  currentImageNum = 0;
  frameRate (12);
  //jumping
  jump = loadImage ("jump.png");
  x = width/2;
  y = height/2;
  image (idle, x, y, 100, 100);
}

void draw () { 
  background (0);
  if (load)
  image (boy, x, y, 100, 100);
}

//keyboard movements
void keyPressed() {
  if (key == CODED) {
     if (keyCode == LEFT) {
       run ();
       load = true;
     } else if(keyCode == RIGHT) {
       run ();
       load = true;
     } else if(keyCode == UP) {
       boy = loadImage ("jump.png");
       load = true;
     }
  }
}

void keyReleased() {
  if (key == CODED) {
     if (keyCode == LEFT) {
       boy = loadImage ("idle.png");
       load = false;
     } else if(keyCode == RIGHT) {
       boy = loadImage ("idle.png");
       load = false;
     } else if(keyCode == UP) {
       boy = loadImage ("idle.png");
       load = false;
     }
  }
}

void run () {
  image (run [currentImageNum], x, y, 100, 100);
  currentImageNum++;
  if (currentImageNum >= NumofImages)
  currentImageNum = 0;
}

It says Null Pointer Exception. I have also tried replacing what’s inside the void run () with a for loop, but then it simply won’t run.

The for loop in question:

for (int i = 0; i < run.length; i++) {
run[i] = loadImage("run"+i+".png");
}

I’m really at loss at where I went wrong. Any help or pointers is very, very appreciated. Thank you in advance!

this should be


run[i] = loadImage("run"+ str(i)+".png");

1 Like

loadImage is very costly on the processor

I suggest you do ALL your loadImage in setup()

Then, it is unwise to call run() from keyPressed since it would be called only once, when the key goes actually down, and not throughout.

I suggest you have an int variable movementType which is 0 for idle, 1 for run and 2 for jump

then in draw(!) say:

if(movementType==0) {

  image (idle, x, y, 100, 100);
}
else if (movementType==1) {
run(); 
}
else if (movementType==2) {
  image (jump, x, y, 100, 100);
}

Then what you do in keyPressed and keyReleased is to set the movementType=1 or movementType=0 etc. correctly

It also slows the processor down to use image() with 5 parameters instead of 3.

Instead look at resize and only use it in setup().

Chrisir

1 Like

Thank you! The idle and jump works great now, wow.

But I’m still having trouble with the run. I have tried trimming the 5 parameters into 3, but it still says Null Pointer Exception.

Instead look at resize and only use it in setup() .

Can you explain what this part, please? I know what resize is, but I don’t understand how it applies here or how to move it to setup().

Thank you again!

forget about resize for now

This is better with >= run.length

void run () {
  image (run [currentImageNum], x, y, 100, 100);
  currentImageNum++;
  if (currentImageNum >= run.length)  // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
        currentImageNum = 0;
} // func 
1 Like

I think resize has not to do with the error you get

You wrote:

regarding resize:

every time you use image() with 5 parameters instead of 3, processing does an internal resize of the image which is costly on the processor

Therefore,

  • always use image() with parameters only and
  • use resize only once in setup for every image

say in setup()
idle.resize(100, 100);

run[i].resize(100, 100); // in the for loop

1 Like
void run () {
  image (run [currentImageNum], x, y, 100, 100);
  currentImageNum++;
  if (currentImageNum >= run.length)  // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
        currentImageNum = 0;
} // func

Tried this! But still Null Point Exception. Darn, I’m starting to think that image (something, something) only works in void draw (), because it stops working literally anywhere else.

Got it, that makes a lot of sense.

Thanks again!

post your entire code please

1 Like

in which line does the error occur?

  image (run [currentImageNum], x, y, 100, 100);
1 Like

image (run [currentImageNum], x, y, 100, 100);

This line right here.

PImage idle;
PImage [] run = new PImage [4];
int currentImageNum;
int NumofImages = 4;
PImage jump; 
int movementType;
int x;
int y;

void setup ()
{
  size (1280, 720);
  smooth ();
  noCursor ();
  //standing
  idle = loadImage ("idle.png");
  idle.resize (100, 100);
  //running
  run [0] = loadImage ("run0.png");
  run [1] = loadImage ("run1.png");
  run [2] = loadImage ("run2.png");
  run [3] = loadImage ("run3.png");
  currentImageNum = 0;
  run[i].resize (100, 100);
  frameRate (12);
  //jumping
  jump = loadImage ("jump.png");
  jump.resize (100, 100);
  x = width/2;
  y = height/2;
}

void draw ()
{ 
  background (0);
  boy.move ();
  boy.display ();
  
  if(movementType == 0) {
  image (idle, x, y, 100, 100);
  }
  else if (movementType == 1) {
  run(); 
  }
  else if (movementType == 2) {
  image (jump, x, y, 100, 100);
}
}

//keyboard movements

void keyPressed() {
  if (key == CODED) {
     if (keyCode == LEFT) {
       movementType = 1;
     } else if(keyCode == RIGHT) {
       run ();
       movementType = 1;
     } else if(keyCode == UP) {
       movementType = 2;
     }
  }
}

void keyReleased() {
  if (key == CODED) {
     if (keyCode == LEFT) {
       movementType = 0;
     } else if(keyCode == RIGHT) {
       movementType = 0;
     } else if(keyCode == UP) {
       movementType = 0;
     }
  }
}

//run
void run () {
  image (run [currentImageNum], x, y);
  currentImageNum++;
  if (currentImageNum >= run.length)  
  currentImageNum = 0;
}

Ta-da. I can post the images I use, too, if it’ll help.

Remark

this line:

run[i].resize (100, 100);

works inside this only:

  for (int i = 0; i < run.length; i++) {
       run[i] = loadImage("run"+str(i)+".png");
  }

Remark

not sure what you do here

  boy.move ();
  boy.display ();

but when this is the boy running or jumping the stuff we discuss should definitely be inside the class, method display()…

Remark

delete run() here

    else if (keyCode == RIGHT) {
      run ();
      movementType = 1;
    }

Remark

also when saying image(idle…) remove 4th and 5th parameter

Remark

code is running on my computer

maybe you renamed an image?

I used this:


  //running
  for (int i = 0; i < run.length; i++) {
    run[i] = loadImage("run"+str(i)+".png");
    println("run"+str(i)+".png"); 
    run[i].resize (100, 100);
  }

Warm regards,

Chrisir

Full Sketch


PImage idle;
PImage [] run = new PImage [4];
int currentImageNum;
int NumofImages = 4;
PImage jump; 
int movementType;
int x;
int y;

void setup ()
{
  size (1280, 720);
  smooth ();
  noCursor ();
  frameRate (12);

  //standing
  idle = loadImage ("idle.png");
  idle.resize (100, 100);

  //running
  for (int i = 0; i < run.length; i++) {
    run[i] = loadImage("run"+str(i)+".png");
    println("run"+str(i)+".png"); 
    run[i].resize (100, 100);
  }
  currentImageNum = 0;

  //jumping
  jump = loadImage ("jump.png");
  jump.resize (100, 100);

  x = width/2;
  y = height/2;
}

void draw ()
{ 
  background (0);
  //boy.move ();
  //boy.display ();

  if (movementType == 0) {
    image (idle, x, y);
  } else if (movementType == 1) {
    run();
  } else if (movementType == 2) {
    image (jump, x, y);
  }
}

//keyboard movements

void keyPressed() {
  if (key == CODED) {
    if (keyCode == LEFT) {
      movementType = 1;
    } else if (keyCode == RIGHT) {
      // run ();
      movementType = 1;
    } else if (keyCode == UP) {
      movementType = 2;
    }
  }
}

void keyReleased() {
  if (key == CODED) {
    if (keyCode == LEFT) {
      movementType = 0;
    } else if (keyCode == RIGHT) {
      movementType = 0;
    } else if (keyCode == UP) {
      movementType = 0;
    }
  }
}

//run
void run () {
  image (run [currentImageNum], x, y);
  currentImageNum++;
  if (currentImageNum >= run.length) {
    currentImageNum = 0;
  }
}
//
1 Like

It worked with a few modifications, thanks so much!!! This really helped!

I’ll post the full working version here in case anyone needs it:

PImage idle;
PImage [] run = new PImage [4];
int currentImageNum;
int NumofImages = 4;
PImage jump; 
int movementType;
int x;
int y;

void setup ()
{
  size (1280, 720);
  smooth ();
  noCursor ();
  frameRate (12);

  //standing
  idle = loadImage ("idle.png");
  idle.resize (100, 100);

  //running
  run [0] = loadImage ("run0.png");
  run [1] = loadImage ("run1.png");
  run [2] = loadImage ("run2.png");
  run [3] = loadImage ("run3.png");
  for (int i = 0; i < run.length; i++) {
  run[i] = loadImage("run"+str(i)+".png");
  println("run"+str(i)+".png"); 
  run[i].resize (100, 100);
  }
  currentImageNum = 0;

  //jumping
  jump = loadImage ("jump.png");
  jump.resize (100, 100);

  x = width/2;
  y = height/2;
}

void draw ()
{ 
  background (0);
  if (movementType == 0) {
    image (idle, x, y);
  } else if (movementType == 1) {
    image (run [currentImageNum], x, y);
    currentImageNum++;
      if (currentImageNum >= run.length) {
      currentImageNum = 0; }
  } else if (movementType == 2) {
    image (jump, x, y);
  }
}

//keyboard movements

void keyPressed() {
  if (key == CODED) {
    if (keyCode == LEFT) {
      movementType = 1;
    } else if (keyCode == RIGHT) {
      movementType = 1;
    } else if (keyCode == UP) {
      movementType = 2;
    }
  }
}

void keyReleased() {
  if (key == CODED) {
    if (keyCode == LEFT) {
      movementType = 0;
    } else if (keyCode == RIGHT) {
      movementType = 0;
    } else if (keyCode == UP) {
      movementType = 0;
    }
  }
}

Thanks again!!

1 Like

Question: Are the images the same whether he runs left or right?

Because normally you would have different ones. Or you could just mirror them in another program like irfanview and load them.

Instead of using 0,1,2 you could name the numbers and refer to the names.

Then you would have something like

if(movementType==typeRun) …

And before setup

final int typeRun = 1;

1 Like

Oh wow, you read my mind. I was just trying to figure out how to mirror the image. So far I’m trying to use scale to mirror the image (scale(direction, 1); in normal and scale(direction, -1); when flipped or something), but no luck so far.

I’m really sorry, but I don’t quite get why this makes a difference? Can you explain why, please?

Thank you again!

As I said, just mirror it in irfanview or photoshop and work with 2 sets of 4 images.

It’s more of the style of your code and the readability of your code.

Instead of
if(movementType==0) … // which is a bit obscure....
you would have

if(movementType==typeRun) … // nice to read

1 Like

Tried this, thanks! It runs fine, but it doesn’t actually flip around when I press the related keys.

I did this for setup () (also, I made this into a seperate player class because it’s getting complicated when I want to add platforms and such):

  //running
  b.run [0] = loadImage ("run0.png");
  b.run [1] = loadImage ("run1.png");
  b.run [2] = loadImage ("run2.png");
  b.run [3] = loadImage ("run3.png");
  for (int i = 0; i < b.run.length; i++) {
  b.run[i] = loadImage("run"+str(i)+".png");
  println("run"+str(i)+".png"); 
  b.run[i].resize (100, 100);
  }
  currentImageNum = 0;
  
  //running left
  b.runleft [0] = loadImage ("runleft0.png");
  b.runleft [1] = loadImage ("runleft1.png");
  b.runleft [2] = loadImage ("runleft2.png");
  b.runleft [3] = loadImage ("runleft3.png");
  for (int i = 0; i < b.runleft.length; i++) {
  b.runleft[i] = loadImage("run"+str(i)+".png");
  println("runleft"+str(i)+".png"); 
  b.runleft[i].resize (100, 100);
  }
  currentImageNum = 0;

And I did this for the void move ():

void moveb () {
    if (movementType == 0) {
    image (b.idle, b.x, b.y);
  } else if (movementType == 1) {
    image (b.run [currentImageNum], b.x, b.y);
    currentImageNum++;
      if (currentImageNum >= b.run.length) {
      currentImageNum = 0; }
  } else if (movementType == 2) {
    image (b.runleft [currentImageNum], b.x, b.y);
    currentImageNum++;
      if (currentImageNum >= b.runleft.length) {
      currentImageNum = 0; }
  } else if (movementType == 3) {
    image (b.jump, b.x, b.y);
  }

And then referenced each to the proper keys (I think), but it doesn’t work. I’m also having trouble with making the character jump, but that’s another problem altogether I think.

I tried making it into typeIdle, typeRun, etc, but it just says that can’t find (typeName). It’s alright though, this is no biggie. I just have to keep in mind which is which, it doesn’t actually interfere with the program.

Thank you in advance, as always!

Here is your error

Besides: when you have a for loop you don’t really need the 4 loadImage lines before

1 Like

Without seeing your boy class:

Try to put as much logic (lines) that belong to the class put inside the class.

E.g. the loadImage belongs into the constructor.

The run() and jump() should be functions in the class

1 Like

…that makes a lot of sense and I’m baffled how I didn’t notice that before. Thank you.

You mean the I should be making a separate void for run and jump? If I’m making a different direction for every jump/run/idle, would that make it too complicated?

Because I’m trying to make it so that, for example, we stop running after running left, the character would still be facing left it would still be facing left in its idle state. Also, on that note, can I ask for advice on how to do that? So far I got this:

class Boy {
  PImage idle;
  PImage idleleft;  
  PImage [] run = new PImage [4];
  PImage [] runleft = new PImage [4];
  int NumofImages = 4;
  PImage jump; 
  PImage jumpleft;
  int x, y;       //boy position
  int xspd, yspd; //boy speed
}

 Boy b;
 int currentImageNum;  
 int ground;
 int gravity = 1;
 int movementType;
 int up;
 
void setup ()
{
  size (1280, 720);
  smooth ();
  noCursor ();
  frameRate (8);
  
  b = new Boy ();
  
  //standing
  b.idle = loadImage ("idle.png");
  b.idle.resize (100, 100);
  
  //standing left
  b.idleleft = loadImage ("idleleft.png");
  b.idleleft.resize (100, 100);
  
  //running
  for (int i = 0; i < b.run.length; i++) {
  b.run[i] = loadImage("run"+str(i)+".png");
  println("run"+str(i)+".png"); 
  b.run[i].resize (100, 100);
  }
  currentImageNum = 0;
  
  //running left
  for (int i = 0; i < b.runleft.length; i++) {
  b.runleft[i] = loadImage("runleft"+str(i)+".png");
  println("runleft"+str(i)+".png"); 
  b.runleft[i].resize (100, 100);
  }
  currentImageNum = 0;
  
  //jumping
  b.jump = loadImage ("jump.png");
  b.jump.resize (100, 100);
  
  //jumping left
  b.jumpleft = loadImage ("jumpleft.png");
  b.jumpleft.resize (100, 100);
  
  ground = height/2;
  b.x = width/2 - 10;
  b.y = ground;
  b.xspd = 0;
  b.yspd = 0;
}

void draw ()
{ 
  background (0);
  moveb ();
}

void moveb () {
//idle
    if (movementType == 0) { 
    image (b.idle, b.x, b.y);
//idle left
  } else if (movementType == 2) {
    image (b.idleleft, b.x, b.y);  
//run   
  } else if (movementType == 3) {
    image (b.run [currentImageNum], b.x, b.y);
    currentImageNum++;
      if (currentImageNum >= b.run.length) {
      currentImageNum = 0; }
//run left
  } else if (movementType == 4) {
    image (b.runleft [currentImageNum], b.x, b.y);
    currentImageNum++;
      if (currentImageNum >= b.runleft.length) {
      currentImageNum = 0; }
//jump
  } else if (movementType == 5) {
    image (b.jump, b.x, b.y);
//jump left
  } else if (movementType == 6) {
    image (b.jumpleft, b.x, b.y);    
  }
   
    if (movementType == 3) b.x = b.x + b.xspd;    
    if (movementType == 4) b.x = b.x - b.xspd;
    if (movementType == 5) b.y = b.y + b.yspd;
    if (movementType == 6) b.y = b.y + b.yspd;
    
//if boy is above ground, apply gravity (reminder that - is up)
    if (b.y < ground) {
    b.yspd = b.yspd + gravity;
  } 
  
//if boy is on the ground and up key is pressed, jump!
    if (b.y == ground &&  up != 0) {
    b.yspd = -5;
  } else {
    b.yspd = 0;
  }
}

//keyboard movements
void keyPressed() {
  if (key == CODED) {
     if (keyCode == LEFT) {
      movementType = 4;
    } else if (keyCode == RIGHT) {
      movementType = 3;
    } else if (keyCode == UP) {
      movementType = 5;
      up = 1;
    }
  }
}


void keyReleased() {
  if (key == CODED) {
      if (keyCode == RIGHT) {
      movementType = 0;
    } else if (keyCode == LEFT) {
      movementType = 1;
    } else if (keyCode == UP) {
      movementType = 0;
      up = 0;
    }
  }
}

But for some reason the image just disappears if I release the left key.

Thanks for all the time you spend helping me, I really appreciate it.

1 Like

Remark about your class

Are you working in the processing IDE? Or in Eclipse?

because the general architecture of a Sketch (pseudo code)

Boy b;  // !!!!!!!!!!
...other variables...

setup() {
  size (1280, 720);
  smooth ();
  noCursor ();
  frameRate (8);
  ground = height/2;

  b = new Boy ();
}

draw() {
}

// =====================================

class Boy {

....boy variables....

  PImage idle;
  PImage idleleft;  
  PImage [] run = new PImage [4];
  PImage [] runleft = new PImage [4];
  int NumofImages = 4;
  PImage jump; 
  PImage jumpleft;
  int x, y;       //boy position
  int xspd, yspd; //boy speed

//constr
Boy() {

  // b. IS NOT NEEDED here.... *****************************************

 //standing
  b.idle = loadImage ("idle.png");
  b.idle.resize (100, 100);
  
  //standing left
  b.idleleft = loadImage ("idleleft.png");
  b.idleleft.resize (100, 100);
  
  //running
  for (int i = 0; i < b.run.length; i++) {
  b.run[i] = loadImage("run"+str(i)+".png");
  println("run"+str(i)+".png"); 
  b.run[i].resize (100, 100);
  }
  currentImageNum = 0;
  
  //running left
  for (int i = 0; i < b.runleft.length; i++) {
  b.runleft[i] = loadImage("runleft"+str(i)+".png");
  println("runleft"+str(i)+".png"); 
  b.runleft[i].resize (100, 100);
  }
  currentImageNum = 0;
  
  //jumping
  b.jump = loadImage ("jump.png");
  b.jump.resize (100, 100);
  
  //jumping left
  b.jumpleft = loadImage ("jumpleft.png");
  b.jumpleft.resize (100, 100);
  
  b.x = width/2 - 10;
  b.y = ground;
  b.xspd = 0;
  b.yspd = 0;

}

void move() {

}

void display() {

}

...other functions in the class....

}// class

Explanation

It’s confusing that you have a Boy b INSIDE the class and a setup() inside the class.

Inside the class you don’t have to say b. …

There is a tutorial about objects: Objects / Processing.org

You should read it

Remark 2

My point was to put more stuff inside the class.

Here you have a lot of references to the object b:

void moveb () {
    if (movementType == 0) {
    image (b.idle, b.x, b.y);
  } else if (movementType == 1) {
    image (b.run [currentImageNum], b.x, b.y);

When this is outside the class I would make it

void moveb () {
    b.move(); 
    ...

or just call b.move(); from draw() (the draw() ouside the class)

This is a good example why I said use typeRun etc. instead of 0,1,2… [I know you tried, but it didn’t work] (using variables with a proper name like typeRun (as opossed to 0,1,2…) let’s you spot errors faster…)

using 0 and 2 here:

using 0 and 1

! must be the same in both code sections !

Chrisir

1 Like