Creating a Big Background Image That a Player Can Navigate and the Camera Follows Them

So I'm trying to create an RPG game for my Computer Science class and I want to make the camera follow my sprite around the canvas and have one big background image that only shows a portion on the canvas and as the user moves it goes to a new part of the image. Essentially creating the map for my game which the player can navigate.

Kind of like this but in 2D:

Currently, I am drawing each scene after the player passes the edge and resizing the canvas to fit the image. This isn't too good because it displaces the window and the user has to keep moving it in order to see the full map. Please help.

This is the code I have so far:

    PImage [] jerryWalking;
    PImage bg;
    int jerryFrames = 4;
    String currentDirection = "right";
    Boolean left, right, up, down ;
    int currentFrame = 0;
    Boolean walking = false;
    int speed = 2;
    int frameSpeed = 200;
    float timeSinceLastFrame;
    int bottomBorder = 250;
    int topBorder = 1;
    int scene = 1;
    Jerry j = new Jerry(200, 200, 32, 32);

    void loadAssets () {
      jerryWalking = new PImage[jerryFrames];
      for ( int i = 0; i<jerryFrames; i++) {
        jerryWalking[i]=loadImage("jerry_"+currentDirection+(i+1)+".png");
      }
    }

    void setup () {
      surface.setResizable(true);
      left = false;
      right = false;
      down = false;
      up = false;
      timeSinceLastFrame = millis();
      bg = loadImage("scene1.png");
    }
    void draw () {
      surface.setSize(bg.width, bg.height);
      image(bg, 0, 0);
      loadAssets();
      j.display();
      j.update();
      println(j.x);
      println();
      println(j.y);
      // left 1094
      // right 1118
    }


    void keyPressed () {
      switch(keyCode) {
      case 37:
        left = true;
        currentDirection = "left";
        walking = true;
        break;
      case 39:
        right = true;
        currentDirection = "right";
        walking = true;
        break;
      case 38:
        up = true;
        currentDirection = "up";
        walking = true;
        break;
      case 40:
        currentDirection = "down";
        down = true;
        walking = true;
        break;
      }
    }

    void keyReleased () {
      switch(keyCode) {
      case 37:
        left = false;
        currentDirection = "left";
        walking = false;
        break;
      case 39:
        right = false;
        currentDirection = "right";
        walking = false;
        break;
      case 38:
        up = false;
        currentDirection = "up";
        walking = false;
        break;
      case 40:
        down = false;
        currentDirection = "down";
        walking = false;
        break;
      case 70:
        speed--;
        break;
      case 71:
        speed++;
        break;
      }
    }

Answers

  • I think it’s called ViewPort

    Try to search this in the forum

  • Answer ✓

    You can have a image which is much bigger than the canvas

    Only a small portion is visible

    Think of an image that is 5000x5000 and your canvas is 1000x800 then you‘d display the image at -2000, -2000 (imgx,imgy)

    Now when the player moves he stays in the center and the map moves (changing imgx,imgy)

  • edited February 2018

    Thank you!

  • edited February 2018 Answer ✓

    @SantiArr16 -- for extremely large maps, where you must load new portions as you move in any direction, one general approach is to use a tilemap.

    There are a lot of tools out there to support this kind of approach

    There are also many tutorials. Most are not specific to Processing, but if there are in Java they may be very similar.

    See also previous RPG projects on the forums, such as https://forum.processing.org/two/discussion/26086/a-quick-beta-build-of-a-adventure-maze-game-based-around-the-minotaur-s-labyrinth

  • @Chrisir I'm having trouble understanding the viewport example that you provided me, I don't really know how to implement this into my game. Is there any tutorial online that could help me?

  • I am not sure about your skill level.

    Do you have an image as background? Is it a jpg file? Which size is it?

    see

    https://en.wikipedia.org/wiki/Viewport

  • can you post your class Jerry and upload the images somewhere?

  • I made an example where I create an internal image (map) artificially and then the player runs over it

    then the map is treated via a viewPort

    solution for right and left ONLY

    // https : // forum.processing.org/two/discussion/26491/creating-a-big-background-image-that-a-player-can-navigate-and-the-camera-follows-them#latest
    // 27/2/2018
    
    PImage [] jerryWalking;
    
    PGraphics bg;
    
    int jerryFrames = 4;
    String currentDirection = "right";
    Boolean left, right, up, down ;
    int currentFrame = 0;
    Boolean walking = false;
    int speed = 2;
    int frameSpeed = 200;
    float timeSinceLastFrame;
    int bottomBorder = 250;
    int topBorder = 1;
    int scene = 1;
    
    Jerry j = new Jerry();//(200, 200, 32, 32);
    
    float viewPortX, viewPortY;
    
    //-----------------------------------------------------------------
    
    void setup () {
    
      size(800, 800); 
    
      surface.setResizable(true);
      left = false;
      right = false;
      down = false;
      up = false;
      timeSinceLastFrame = millis();
    
      // bg = loadImage("scene1.png");
    
      //if(bg==null) {
      if (true) {
        bg= createGraphics(5000, 5000);
        bg.beginDraw();
        bg.background(100);
        bg.stroke(255);
        for (int x=0; x < bg.width; x+=50) {
          for (int y=0; y < bg.height; y+=50) {
            bg.fill(random(255));
            bg.rect(x, y, 50, 50);
          }
        }// end of for
        bg.endDraw();
      }//if
    
      loadAssets();
    }//func 
    
    void draw () {
      //  surface.setSize(bg.width, bg.height);
    
      image(bg, viewPortX, viewPortY);
    
      j.display();
      j.update();
    
      print(j.x);
      print(",");
      println(j.y);
      // left 1094
      // right 1118
    
      // manage viewPort -----------------
    
    
      if (j.x<60) // player 
        viewPortX+=5;
    
      // checks X  
      if (viewPortX > bg.width-width)
        viewPortX=bg.width-width;
    
      if (viewPortX < width-bg.width)
        viewPortX=width-bg.width;
    
      if (viewPortX < -bg.width+width)
        viewPortX=-bg.width+width;
    
      if (viewPortX > 0)
        viewPortX=0;
    }
    
    //-----------------------------------------------------------------------
    
    void loadAssets () {
      jerryWalking = new PImage[jerryFrames];
      for ( int i = 0; i<jerryFrames; i++) {
        jerryWalking[i]=loadImage("jerry_"+currentDirection+(i+1)+".png");
      }
    }
    
    // --------------------------------------------------------------------
    
    void keyPressed () {
      switch(keyCode) {
      case 37:
        left = true;
        currentDirection = "left";
        walking = true;
        break;
      case 39:
        right = true;
        currentDirection = "right";
        walking = true;
        break;
      case 38:
        up = true;
        currentDirection = "up";
        walking = true;
        break;
      case 40:
        currentDirection = "down";
        down = true;
        walking = true;
        break;
      }
    }
    
    void keyReleased () {
      switch(keyCode) {
      case 37:
        left = false;
        currentDirection = "left";
        walking = false;
        break;
      case 39:
        right = false;
        currentDirection = "right";
        walking = false;
        break;
      case 38:
        up = false;
        currentDirection = "up";
        walking = false;
        break;
      case 40:
        down = false;
        currentDirection = "down";
        walking = false;
        break;
      case 70:
        speed--;
        break;
      case 71:
        speed++;
        break;
      }
    }
    
    // ================================================================
    
    class Jerry {
    
      float x=110, 
        y=110;
    
      void display() {
        ellipse(x, y, 13, 14);
      }
    
      void update() {
        if (right) {
    
          if (viewPortX <= width-bg.width) {
            viewPortX=width-bg.width;
            x+=4;
          } else {
            if (j.x>width-60) // player 
              viewPortX-=5;
            else 
            x+=4;
          }
        }//right 
    
        if (left) {
          if (viewPortX >= 0) {
            viewPortX=0;
            x-=4;
          } else {
            if (j.x<60) // player 
              viewPortX+=5;
            else 
            x-=4;
          }
        }//left 
    
    
        if (up)
          y-=4;
    
        if (down)
          y+=4;
    
        //-----------------
        // checks
    
        if (x>width-7)
          x=width-7;
    
        if (x<7)
          x=7;
      }
    }//class 
    //
    
  • As you can see, the map / entire dungeon is 5000x5000 pixels, much bigger than the canvas. (You had it the same size)

    The two viewPort variables show this map and move the map so that the player is always inside the viewPort.

    The viewPort variables are negative values so that the map is shown very far left and above the screen so that the canvas can show the right part of the map.

    Imagine the canvas / monitor stays where it is and you move the huge map underneath it left and right , up and down

  • Thank you, I will look at the code you provided me and I will try to implement it. I'm currently in school (where imgur is blocked) so I can't really upload them anywhere so, if you could provide me with your email, I could send them to you. Also here is my Jerry class.

            class Jerry {
    
    
    
              int x;
    
              int y;
    
              int speedX;
    
              int speedY;
    
              int w;
    
              int h;
    
    
    
              Jerry (int jx, int jy, int jW, int jH) {
    
                x = jx;
    
                y = jy;
    
                speedX = 0;
    
                speedY = 0;
    
                w = jW;
    
                h = jH;
    
              }
    
    
    
              // Displays each frame depending on what direction it is facing.
    
              void display () {
    
                image(jerryWalking[currentFrame], x, y, w, h);
    
                if (speedX == speed) {
    
                  currentDirection = "right";
    
                  if (walking) {
    
                    if (millis ()- timeSinceLastFrame >= frameSpeed) {
    
                      timeSinceLastFrame = millis();
    
                      currentFrame++;
    
                      if (currentFrame == jerryFrames) {
    
                        currentFrame =0;
    
                      }
    
                    }
    
                  }
    
                }
    
                if (speedX == -1 *speed) {
    
                  currentDirection = "left";
    
                  if (walking) {
    
                    if (millis ()- timeSinceLastFrame >= frameSpeed) {
    
                      timeSinceLastFrame = millis();
    
                      currentFrame++;
    
                      if (currentFrame == jerryFrames) {
    
                        currentFrame =0;
    
                      }
    
                    }
    
                  }
    
                }
    
                if (speedY == speed) {
    
                  currentDirection = "down";
    
                  if (walking) {
    
                    if (millis ()- timeSinceLastFrame >= frameSpeed) {
    
                      timeSinceLastFrame = millis();
    
                      currentFrame++;
    
                      if (currentFrame == jerryFrames) {
    
                        currentFrame =0;
    
                      }
    
                    }
    
                  }
    
                }
    
                if (speedY == -1 * speed) {
    
                  currentDirection = "up";
    
                  if (walking) {
    
                    if (millis ()- timeSinceLastFrame >= frameSpeed) {
    
                      timeSinceLastFrame = millis();
    
                      currentFrame++;
    
                      if (currentFrame == jerryFrames) {
    
                        currentFrame =0;
    
                      }
    
                    }
    
                  }
    
                }
    
              }
    
    
    
              // Allows the sprite to walk.
    
              void update () {
    
                if (left) {
    
                  speedX = -1 * speed;
    
                }
    
                if (right) {
    
                  speedX = speed;
    
                }
    
                if (!left && !right) {
    
                  speedX = 0;
    
                }
    
                if (left && right) {
    
                  speedX = 0;
    
                }
    
                if (up) {
    
                  speedY = -1 * speed;
    
                }
    
                if (down) {
    
                  speedY = speed;
    
                }
    
                if (!down && !up) {
    
                  speedY = 0;
    
                }
    
                if (down && up) {
    
                  speedY = 0;
    
                }
    
                if (left && up) {
    
                  speedY = 0;
    
                }
    
                if (right && up) {
    
                  speedY = 0;
    
                }
    
                if (right && down) {
    
                  speedY = 0;
    
                }
    
                if (left && down) {
    
                  speedY = 0;
    
                }
    
                x+=0;
    
                y+=0;
    
              }
    
            }
    

    I currently have an image as my background , its not a tile map. It seems to be working fine. Thanks again. This is my updated code, if you know of any way that I can send you the images please let me know ASAP.

            import gifAnimation.*;
    
    
    
            PImage [] jerryWalking;
    
            PImage [] allFireFrames;
    
            Gif fire;
    
            PImage bg;
    
            int jerryFrames = 4;
    
            String currentDirection = "right";
    
            Boolean left, right, up, down ;
    
            int currentFrame = 0;
    
            Boolean walking = false;
    
            int speed = 2;
    
            int frameSpeed = 200;
    
            float timeSinceLastFrame;
    
            int bottomBorder = 250;
    
            int topBorder = 1;
    
            int scene = 1;
    
            int bgX =-1669;
    
            int bgY = -522;
    
            int scrollSpeed = 4;
    
            int offsetX = 0;
    
            int offsetY = 0;
    
            boolean isOut;
    
            Jerry j = new Jerry(200, 200, 32, 32);
    
            Border b;
    
    
    
            // Loads the animation frames for your character.
    
            void loadAssets () {
    
              jerryWalking = new PImage[jerryFrames];
    
              for ( int i = 0; i<jerryFrames; i++) {
    
                jerryWalking[i]=loadImage("jerry_"+currentDirection+(i+1)+".png");
    
              }
    
            }
    
    
    
            void setup () {
    
              //surface.setResizable(true);
    
              frameRate(60);
    
              size (500, 500);
    
              left = false;
    
              right = false;
    
              down = false;
    
              up = false;
    
              timeSinceLastFrame = millis();
    
              allFireFrames = Gif.getPImages(this, "fire.gif");
    
              bg = loadImage("map.png");
    
              fire = new Gif(this, "fire.gif");
    
              fire.play();
    
            }
    
            void draw () {
    
              frameRate(30);
    
              background(0);
    
              image(bg, bgX, bgY);
    
              b = new Border(283-offsetX,35-offsetY,196,400);
    
              image(fire, width/2-offsetX-65, height/2-offsetY-30);
    
              image(fire, width/2-offsetX-65, height/2-offsetY+90);
    
              loadAssets();
    
              // Barrabas
    
              j.display();
    
              j.update();
    
              // Border prototype
    
              isOut = b.checkIfOut(j.x+32,j.y);
    
              b.display();
    
              b.keepJin();
    
              println(bgX, bgY);
    
              fill(255);
    
              println(b.wb,b.hb);
    
              println(mouseX,mouseY);
    
              println(isOut);
    
    
    
            }
    
    
    
            //Movement
    
            void keyPressed () {
    
              switch(keyCode) {
    
              case 37:
    
                left = true;
    
                currentDirection = "left";
    
                walking = true;
    
                bgX += scrollSpeed;
    
                offsetX -= scrollSpeed;
    
                break;
    
              case 39:
    
                right = true;
    
                currentDirection = "right";
    
                walking = true;
    
                bgX -= scrollSpeed;
    
                offsetX += scrollSpeed;
    
                break;
    
              case 38:
    
                up = true;
    
                currentDirection = "up";
    
                walking = true;
    
                bgY+= scrollSpeed;
    
                offsetY -= scrollSpeed;
    
                break;
    
              case 40:
    
                currentDirection = "down";
    
                down = true;
    
                walking = true;
    
                bgY -= scrollSpeed;
    
                offsetY += scrollSpeed;
    
                break;
    
              }
    
            }
    
    
    
            void keyReleased () {
    
              switch(keyCode) {
    
              case 37:
    
                left = false;
    
                currentDirection = "left";
    
                walking = false;
    
                break;
    
              case 39:
    
                right = false;
    
                currentDirection = "right";
    
                walking = false;
    
                break;
    
              case 38:
    
                up = false;
    
                currentDirection = "up";
    
                walking = false;
    
                break;
    
              case 40:
    
                down = false;
    
                currentDirection = "down";
    
                walking = false;
    
                break;
    
              case 70:
    
                scrollSpeed--;
    
                break;
    
              case 71:
    
                scrollSpeed++;
    
                break;
    
              }
    
            }
    

    BORDER CLASS :

            class Border {
    
    
    
              int x;
    
              int y;
    
              int w;
    
              int h;
    
              int wb;
    
              int hb;
    
    
    
              Border(int bx, int by, int bw, int bh) {
    
                x = bx;
    
                y = by;
    
                w = bw;
    
                h = bh;
    
                wb = x+w;
    
                hb = y+h;
    
              }
    
    
    
              void display () {
    
                noStroke();
    
                fill(255);
    
                rect(x, y, w, h);
    
              }
    
    
    
              boolean checkIfOut (int mx, int my) {
    
                if ( mx>x && mx<wb && my>y && my<hb) {
    
                  return true;
    
                } else {
    
                  return false;
    
                }
    
              }
    
    
    
              void keepJin () {
    
                if (isOut == true) {
    
                  j.x = x-32;
    
                  scrollSpeed = 0;
    
                }
    
                if (isOut == false) {
    
                scrollSpeed = 4;
    
                }
    
              }
    
            } // end of Border Class
    
  • just implement my solution and use an image as map much bigger than your canvas

Sign In or Register to comment.