🔍

Spritesheet Animation in Processing

simple player created from npc class

This example shows how to use a general NPC class to create a key controlled player character to move over the stage.


// ------------------------------------------------------------
// ----- CUSTOM PLAYER OBJECT DERIVATED BY NPC ----------------
// ------------------------------------------------------------

aNPC playerOBJ;

float medge = .1; // motion edge

void createPlayer(){

  playerOBJ = new aNPC(-1);
  playerOBJ.acc.x = 0;
  playerOBJ.acc.y = 0;

}

// ---------------------------------------------
// ---------------------------------------------
// ----- CUSTOM NPC CLASS ---------------- 
// ---------------------------------------------
// ---------------------------------------------
 
class aNPC{
 
  int id = -1;  
  PVector pos = new PVector(256,128);
  PVector acc = new PVector();
  int animtick = 0;
  int anim_speed = int(random(3,7)); // animation speed // lower = faster
  int[] curr_animation  = new int[1];
  boolean hor_flipped = false;
  
   
  aNPC(int _id){
    id=_id;
    acc.x = random(-1,1);
    acc.y = random(-1,1);
  }
   
  void updateAndDrawMe(){
   
    if(abs(acc.x)>medge){
    
       curr_animation = side_anim;
       hor_flipped = false;
       if(acc.x>medge){
         hor_flipped = true;
       }
    }
    
    
    if(abs(acc.y) > abs(acc.x)){
      if(acc.y>medge){
          
          curr_animation = front_anim;
      }
      
      if(acc.y<-medge){
          curr_animation = back_anim;
      }
    }
    
    
     
    animtick = animate(
      int(pos.x),
      int(pos.y),
      64,64,
      curr_animation,
      animtick,
      int(anim_speed/acc.mag())+1,
      hor_flipped
    );
    
  
    if(pos.x > width || pos.x < 0){ acc.x *= -random(.9,1.1); }
    if(pos.y > height ||pos.y < 0){ acc.y *= -random(.9,1.1);}
     
     pos.x += acc.x;
     pos.y += acc.y;
   
  }
 
}
 
aNPC[] allNPCS;
 
void createNPCS(){
 
  allNPCS = new aNPC[55];
  for(int i=0;i<55;i++){
   
    allNPCS[i] = new aNPC(i);    
  }
 
}
 
// ---------------------------------------------
// ---------------------------------------------
// ----- SPRITESHEET OPERATIONS ---------------- 
// ---------------------------------------------
// ---------------------------------------------

// the default sheet has 9 sprites in it in a 3x3 matrix
// 0 - 1 - 2
// 3 - 4 - 5
// 6 - 7 - 8

int[] front_anim = {6,7,8,7};
int[] back_anim = {0,1,2,1};
int[] side_anim = {3,4,5,4};
 
int DIM_X = 3; // horizontal dimension of spritesheet
int DIM_Y = 3; // vertical dimension of spritesheet
   
// ---------------------------------------------
// ---------------------------------------------
// ---------------------------------------------
 
PImage main_sheet;
PImage[] sprites = new PImage[DIM_X*DIM_Y];
 
 // ---------------------------------------------
void init_spritesheet() {
 imageMode(CENTER);
  main_sheet = loadImage("slime_monster_spritesheet.png");
  int W = main_sheet.width/DIM_X;
  int H = main_sheet.height/DIM_Y;
  
  for (int i=0; i<sprites.length; i++) {
    int x = i%DIM_X*W;
    int y = i/DIM_Y*H;
    sprites[i] = main_sheet.get(x, y, W, H);
  }
}
  
// ---------------------------------------------
 
int animate(int _x, int _y, int _w, int _h, int[] _arr, int _anim_tick, int _anim_speed, boolean _hor_flipped) {
  
  
  pushMatrix();
    translate(_x, _y);
    if(_hor_flipped){
      scale(1,1);
    }else{
      scale(-1,1);
    }
    image(sprites[_arr[_anim_tick]],0,0 , _w, _h);
  popMatrix();
  
  if (frameCount % _anim_speed == 0) {
    if (_anim_tick<_arr.length-1) {
     _anim_tick++;
    } else {
     _anim_tick = 0;
    }
  }
   
  return  _anim_tick;
}
 
  
// ---------------------------------------------
// ---------------------------------------------
// ----- MAIN PART OF THE SCRIPT ---------------- 
// ---------------------------------------------
// ---------------------------------------------
 
// some variables to move an NPC
float xpos = 128;
float ypos = 128;
  
float accx = 1;
float accy = 1;
 
// --------------------------------
 
void setup() {
    
  size(512,256); // set stage size
  init_spritesheet(); // initally setup the spritesheet
  noSmooth();
  createNPCS();
  createPlayer();
  frameRate(60);
}
  
   
// --------------------------------
  
  
void draw() {
  
  background( 22,255,22 ); 
  
  for(int i=0;i<allNPCS.length;i++){
   
     allNPCS[i].updateAndDrawMe();   
  }

   if(keyPressed){ 
     keyAction(); 
   }
  
  playerOBJ.updateAndDrawMe();
  playerOBJ.acc.x *= .8;
  playerOBJ.acc.y *= .8;
  
}

// --------------------------------

void keyAction(){
  
  //println(keyCode);
  
  if(keyCode == 37){  playerOBJ.acc.x = -2; } // left
  if(keyCode == 39){  playerOBJ.acc.x = 2; } // right
  
  if(keyCode == 38){  playerOBJ.acc.y = -2; } // up
  if(keyCode == 40){  playerOBJ.acc.y = 2; } // down
  
}


class implementation to create multiple npc’s with individual animation

The second example comes with an implementation of a more advanced, but necessary class logic. Each NPC is handled with a custom object, created in the background to store position, acceleration, animation tick and speed.


// ---------------------------------------------
// ---------------------------------------------
// ----- CUSTOM NPC CLASS ---------------- 
// ---------------------------------------------
// ---------------------------------------------

class aNPC{

  int id = -1;  
  PVector pos = new PVector(256,128);
  PVector acc = new PVector();
  int animtick = 0;
  int anim_speed = int(random(3,7)); // animation speed // lower = faster
  
  aNPC(int _id){
    id=_id;
    acc.x = random(-1,1);
    acc.y = random(-1,1);
  }
  
  void updateAndDrawMe(){
  
    
    animtick = animate(int(pos.x),int(pos.y), 64,64, front_anim, animtick,anim_speed);
    if(pos.x > width || pos.x < 0){ acc.x *= -random(.9,1.1); }
    if(pos.y > height ||pos.y < 0){ acc.y *= -random(.9,1.1);}
    
     pos.x += acc.x;
     pos.y += acc.y;
  
  }

}

aNPC[] allNPCS;

void createNPCS(){

  allNPCS = new aNPC[55];
  for(int i=0;i<55;i++){
  
    allNPCS[i] = new aNPC(i);    
  }

}

// ---------------------------------------------
// ---------------------------------------------
// ----- SPRITESHEET OPERATIONS ---------------- 
// ---------------------------------------------
// ---------------------------------------------
// the default sheet has 9 sprites in it in a 3x3 matrix
// 0 - 1 - 2
// 3 - 4 - 5
// 6 - 7 - 8
int[] front_anim = {6,7,8,7};
int[] back_anim = {0,1,2,1};

int DIM_X = 3; // horizontal dimension of spritesheet
int DIM_Y = 3; // vertical dimension of spritesheet

 
// ---------------------------------------------
// ---------------------------------------------
// ---------------------------------------------

PImage main_sheet;
PImage[] sprites = new PImage[DIM_X*DIM_Y];

 // ---------------------------------------------
void init_spritesheet() {
 imageMode(CENTER);
  main_sheet = loadImage("slime_monster_spritesheet.png");
  int W = main_sheet.width/DIM_X;
  int H = main_sheet.height/DIM_Y;
 
  for (int i=0; i<sprites.length; i++) {
    int x = i%DIM_X*W;
    int y = i/DIM_Y*H;
    sprites[i] = main_sheet.get(x, y, W, H);
  }
}
 
// ---------------------------------------------

int animate(int _x, int _y, int _w, int _h, int[] _arr, int _anim_tick, int _anim_speed) {
   
  image(sprites[_arr[_anim_tick]], _x, _y, _w, _h);
  if (frameCount % _anim_speed == 0) {
    if (_anim_tick<_arr.length-1) {
     _anim_tick++;
    } else {
     _anim_tick = 0;
    }
  }
  
  return  _anim_tick;
}




// ---------------------------------------------
// ---------------------------------------------
// ----- MAIN PART OF THE SCRIPT ---------------- 
// ---------------------------------------------
// ---------------------------------------------

// some variables to move an NPC
float xpos = 128;
float ypos = 128;
 
float accx = 1;
float accy = 1;

// --------------------------------

void setup() {
   
  size(512,256); // set stage size
  init_spritesheet(); // initally setup the spritesheet
  noSmooth();
  createNPCS();
  frameRate(60);
}
 
  
// --------------------------------
 
 
void draw() {
 
  background( 22,255,22 ); 
 
  for(int i=0;i<allNPCS.length;i++){
  
    allNPCS[i].updateAndDrawMe();   
  }
     
 
}



basic animation example with one npc

This example shows a simple application of animating through a spritesheet with a custom sheet order. The animation is then move across the stage with a simple bouncing algorithm.



DOWNLOAD THE SHEET HERE

// ---------------------------------------------
// ---------SPRITESHEET SETUP ----------------------
// ---------------------------------------------

// the default sheet has 9 sprites in it in a 3x3 matrix
// 0 - 1 - 2
// 3 - 4 - 5
// 6 - 7 - 8
int[] front_anim = {6,7,8,7};
int[] back_anim = {0,1,2,1};
int anim_speed = 5; // animation speed // lower = faster
int DIM_X = 3; // horizontal dimension of spritesheet
int DIM_Y = 3; // vertical dimension of spritesheet

 
// ---------------------------------------------
// ---------------------------------------------
// ---------------------------------------------

PImage main_sheet;
PImage[] sprites = new PImage[DIM_X*DIM_Y];
int anim_tick = 0;

 // ---------------------------------------------
void init_spritesheet() {
 imageMode(CENTER);
  main_sheet = loadImage("slime_monster_spritesheet.png");
  int W = main_sheet.width/DIM_X;
  int H = main_sheet.height/DIM_Y;
 
  for (int i=0; i<sprites.length; i++) {
    int x = i%DIM_X*W;
    int y = i/DIM_Y*H;
    sprites[i] = main_sheet.get(x, y, W, H);
  }
}
 
// ---------------------------------------------

void animate(int _x, int _y, int _w, int _h, int[] _arr ) {
   
  image(sprites[_arr[anim_tick]], _x, _y, _w, _h);
  if (frameCount % anim_speed == 0) {
    if (anim_tick<_arr.length-1) {
      anim_tick++;
    } else {
      anim_tick = 0;
    }
  }
}
 
// ---------------------------------------------
// ---------------------------------------------
// ---------------------------------------------

// some variables to move an NPC
float xpos = 128;
float ypos = 128;
 
float accx = 1;
float accy = 1;

// --------------------------------

void setup() {
   
  size(256,256); // set stage size
  init_spritesheet(); // initally setup the spritesheet
  noSmooth();
  
  frameRate(60);
}
 
  
// --------------------------------
 
 
void draw() {
 
  background( 22,255,22 ); 
  animate(int(xpos),int(ypos), 64,64, front_anim);
   
  xpos += accx;
  ypos += accy;
  
  if(xpos > width || xpos < 0){ accx *= -random(.9,1.1); }
  if(ypos > height || ypos < 0){ accy *= -random(.9,1.1);}
     
}