Use arrow keys to "pull" object, translate keypresses to changes in X, Y, and Z rotation

I have a few questions: Which order is best for XYZ rotation, and how do I transform keypresses to "pull" an object (or some objects) down, up, left, or right? I tried making a perspective vector, but I had no idea what I was doing, and failed. Could someone perhaps attach some code of "pulling" a cube in certain directions by pressing the arrow keys? This would be incredibly useful for a 3-D Minecraft clone, rather than a 2-D version I decided to develop instead.

Tagged:

Answers

  • While writing the main post, I realized that you should rotate the camera in the opposite direction. Not only does it work just as well, it makes it easier to rotate multiple objects, and enables panning and zooming.

  • do you use peasy cam?

  • here is a version with wasd / pl / space bar

    without peasy cam

    float rotationX;
    float rotationY;
    float rotationZ;
    
    float translatex=0;
    float translatey=0;
    float translatez=0;
    
    float z = 1.0;
    
    int objectNumber = 0;
    
    PShape s; 
    
    void setup() {
    
      size(1200, 900, P3D);
      background(0);
      sphereDetail(32);
    
      s = createShape();
      s.beginShape();
      s.fill(255, 0, 0);
      s.stroke(177);
      s.vertex(100, 0, -100);
      s.vertex(-60, 0, 200);
      s.vertex(-50, 60, -30);
      s.vertex(110, 160, 40);
      s.endShape(CLOSE);
    }
    
    void draw() {
      background(0);
      lights();
    
      showObject();
    
      fill(255); // white 
      text ("Use wasd and pl (space bar to toggle object)", 
        19, 19);
    
      if (keyPressed)
        keyPressed_ForContinuusKeyinput();
    }
    
    // -----------------------------------------------------------
    
    void showObject() {
    
      // show object 
    
      pushMatrix();
    
      fill(255, 0, 0); // red
      translate(width/3, height/2, 0);
      translate(translatex-300, translatey-300, translatez-300);
      rotateZ(rotationZ);
      rotateY(rotationY);
      rotateX(rotationX);
      scale(z);
    
      if (objectNumber==0) {
        stroke(111);
        box(100);
      } else if (objectNumber==1) {
        shape(s, 0, 0);
        //rect(-33, -33, 66, 66);
      } else if (objectNumber==2) {
        noStroke();
        sphere(90);
      }//else 
    
      popMatrix();
    }
    
    // -----------------------------------------------------------
    
    void keyPressed_ForContinuusKeyinput() {
      // keyPressed for registering a key throughout  
    
      float speed = 3.9; 
    
      switch(key) {
    
      case 'a':
        translatex-=speed;
        break; 
    
      case 'd':
        translatex+=speed;
        break;
    
      case 'w':
        translatey-=speed;
        break; 
    
      case 's':
        translatey+=speed;
        break;
    
      case 'p':
        translatez-=speed;
        break; 
    
      case 'l':
        translatez+=speed;
        break;
      }//switch
    }//func
    
    // ----------------------------
    
    void keyPressed() {
    
      // keyPressed for registering a single key only 
    
      switch(key) {
      case ' ':
        objectNumber++;
        if (objectNumber>2)
          objectNumber=0;
        key=0;
        break;
      }//switch
      //
    }//func
    //
    
  • new version with full peasy cam support, only 4 lines more ; you can rotate scene with mouse now (you need to install library: Menu | Import library | Add library)

    // with peasy : http://mrfeinberg.com/peasycam/
    
    import peasy.PeasyCam; // new 
    
    PeasyCam cam;
    
    float rotationX;
    float rotationY;
    float rotationZ;
    
    float translatex=0;
    float translatey=0;
    float translatez=0;
    
    float z = 1.0;
    
    int objectNumber = 0;
    
    PShape s; 
    
    void setup() {
    
      size(1200, 900, P3D);
      background(0);
      sphereDetail(32);
    
      cam = new PeasyCam(this, 400); // new 
    
      s = createShape();
      s.beginShape();
      s.fill(255, 0, 0);
      s.stroke(177);
      s.vertex(100, 0, -100);
      s.vertex(-60, 0, 200);
      s.vertex(-50, 60, -30);
      s.vertex(110, 160, 40);
      s.endShape(CLOSE);
    }
    
    void draw() {
      background(0);
      lights();
    
      showObject();
    
      cam.beginHUD(); 
      fill(255); // white 
      text ("Use wasd and pl (space bar to toggle object)", 
        19, 19);
    
      if (keyPressed)
        keyPressed_ForContinuusKeyinput();
    
      cam.endHUD();
    }
    
    // -----------------------------------------------------------
    
    void showObject() {
    
      // show object 
    
      pushMatrix();
    
      fill(255, 0, 0); // red
      translate(width/3, height/2, 0);
      translate(translatex-300, translatey-300, translatez-300);
      rotateZ(rotationZ);
      rotateY(rotationY);
      rotateX(rotationX);
      scale(z);
    
      if (objectNumber==0) {
        stroke(111);
        box(100);
      } else if (objectNumber==1) {
        shape(s, 0, 0);
        //rect(-33, -33, 66, 66);
      } else if (objectNumber==2) {
        noStroke();
        sphere(90);
      }//else 
    
      popMatrix();
    }
    
    // -----------------------------------------------------------
    
    void keyPressed_ForContinuusKeyinput() {
      // keyPressed for registering a key throughout  
    
      float speed = 3.9; 
    
      switch(key) {
    
      case 'a':
        translatex-=speed;
        break; 
    
      case 'd':
        translatex+=speed;
        break;
    
      case 'w':
        translatey-=speed;
        break; 
    
      case 's':
        translatey+=speed;
        break;
    
      case 'p':
        translatez-=speed;
        break; 
    
      case 'l':
        translatez+=speed;
        break;
      }//switch
    }//func
    
    // ----------------------------
    
    void keyPressed() {
    
      // keyPressed for registering a single key only 
    
      switch(key) {
      case ' ':
        objectNumber++;
        if (objectNumber>2)
          objectNumber=0;
        key=0;
        break;
      }//switch
      //
    }//func
    //
    
  • edited April 2018 Answer ✓

    in this version you have the current box/sphere/folded triangle shape that you can move. With return you can add it to the list of all objects.

    This version makes use of a class "Box3D" and of objects that are stored in an ArrayList boxes. For class and objects see tutorial:

    https://www.processing.org/tutorials/objects/

    • you need to install library peasy cam: Menu | Import library | Add library.

    Chrisir ;-)

    // https : // forum.processing.org/two/discussion/27564/use-arrow-keys-to-pull-object-translate-keypresses-to-changes-in-x-y-and-z-rotation#latest
    // with peasy : http://mrfeinberg.com/peasycam/
    
    import peasy.PeasyCam;
    
    PeasyCam cam;
    
    // ArrayLists for the class
    ArrayList<Box3D> boxes = new ArrayList(); 
    
    // this is a very important size: size boxes
    float sizeBox = 70.0;
    
    float rotationX;
    float rotationY;
    float rotationZ;
    
    float translatex=-sizeBox;
    float translatey=-sizeBox;
    float translatez=0;
    
    float z = 1.0;
    
    int objectNumber = 0;
    
    // folded triangle 
    PShape s; 
    
    float offsetZ=400;
    
    color colCurrent=color(0, 0, 255);
    
    boolean showHelpText=true; 
    
    void setup() {
    
      size(1200, 900, P3D);
      background(0);
      sphereDetail(32);
    
      cam = new PeasyCam(this, 400); // new 
    
      s = createShape();
      s.beginShape();
      s.fill(255, 0, 0);
      s.stroke(177);
      s.vertex(100, 0, -100);
      s.vertex(-60, 0, 200);
      s.vertex(-50, 60, -30);
      s.vertex(110, 160, 40);
      s.scale(0.4);
      s.endShape(CLOSE);
    }
    
    void draw() {
      background(0);
      lights();
    
      for (Box3D b : boxes ) {
        b.display();
      }
    
      showObject();
    
      cam.beginHUD(); 
      if (showHelpText) {
        fill(255); // white 
        text ("Use PeasyCam. Use wasd and pl; r/g/b for colors, space bar to toggle object, RETURN to enter object to list.\n\n"
          +"Use x to switch this text on and off. \n"
          +"Use Mousewheel to zoom, double click to reset view. The box you move has a white outline. An item that has been added to the list is marked with a gray box.\n"
          +"On adding to the list the placing box moves two units left. \n"
          +"Current: "
          +translatex
          +", "
          +translatey
          +", "
          +translatez, 
          19, 19);
      }
    
      if (keyPressed)
        keyPressed_ForContinuusKeyinput();
    
      cam.endHUD();
    }
    
    // -----------------------------------------------------------
    
    void showObject() {
    
      // show object 
    
      pushMatrix();
    
      fill(colCurrent); // color
      //translate(width/3, height/2, 0);
      translate(translatex, translatey, translatez);
      //rotateZ(rotationZ);
      //rotateY(rotationY);
      //rotateX(rotationX);
    
      stroke(255); 
    
      if (objectNumber==0) {
        //stroke(111);
        box(sizeBox);
      } else if (objectNumber==1) {
        s.setFill(colCurrent);
        shape(s, 0, 0);
      } else if (objectNumber==2) {
        //noStroke();
        sphere(sizeBox);
      }//else 
    
      popMatrix();
    }
    
    // -----------------------------------------------------------
    
    void keyPressed_ForContinuusKeyinput() {
      // keyPressed for registering a key throughout  
    
      float speed = 3.9; 
    
      switch(key) {
    
      case 'a':
        translatex-=speed;
        break; 
    
      case 'd':
        translatex+=speed;
        break;
    
      case 'w':
        translatey-=speed;
        break; 
    
      case 's':
        translatey+=speed;
        break;
    
        // --
    
      case 'p':
        translatez-=speed;
        break; 
    
      case 'l':
        translatez+=speed;
        break;
    
        // -----
    
        //
      }//switch
      //
    }//func
    
    // ----------------------------
    
    void keyPressed() {
    
      // keyPressed for registering a single key only 
    
      switch(key) {
      case ' ':
        objectNumber++;
        if (objectNumber>2)
          objectNumber=0;
        key=0;
        break;
    
      case RETURN:
      case ENTER:
        Box3D newBox = new Box3D (
          translatex, 
          translatey, 
          translatez, 
          rotationY, 
          objectNumber, 
          colCurrent
          ); 
        boxes.add(newBox); 
        translatex-=sizeBox*2;
        break;
    
        // ---
        //colors
    
      case 'r':
        colCurrent=color(255, 0, 0);
        break;
    
      case 'b':
        colCurrent=color(0, 0, 255);
        break;
    
      case 'g':
        colCurrent=color(0, 255, 0);
        break;
    
        // --
    
      case 'x':
        showHelpText = !showHelpText; 
        break;
      }//switch
      //
    } //func
    
    //=============================================================
    
    class Box3D {
    
      float x=mouseX, y=410, z=mouseY;
    
      float offsetX=0; 
      float offsetZ2=0; 
    
      float rotateY=0;
      color colBox = color(255, 0, 0);
    
      // type
      final int boxTypeUndefined = -1;
      final int boxType0   = 0;
      final int boxType1   = 1; 
      final int boxType2   = 2; 
      // more needed !!!!! ???? 
      int type = boxTypeUndefined; 
    
      // constr - full
      Box3D(float x_, 
        float y_, 
        float z_, 
        float rotateY_, 
        int type_, 
        color col1_) {
        //
    
        x=x_;
        y=y_;
        z=z_;
    
        rotateY= rotateY_;
        type=type_;
    
        colBox=col1_;
      } // constr 2 
    
      void display() {
        pushMatrix();
    
        translate(x, y, z);
        //  rotateY(radians(rotateY)); 
    
        fill(colBox);
        noStroke(); 
    
        // depending on the current buttons tag 
        // 
        switch (type) {
    
        case boxTypeUndefined:
          // undefined 
          break; 
    
        case boxType0: 
          stroke(111);
          box(sizeBox);
          break; 
    
        case boxType1: 
          s.setFill(colBox);
          shape(s, 0, 0);
          break;
    
        case boxType2: 
          noStroke();
          sphere(sizeBox);
          break;
    
        default:
          // error 
          break;
        }//switch 
    
        if (showHelpText) {
          translate(sizeBox/2, -sizeBox/2, sizeBox/2);
    
          fill(111);
          stroke(0);
          box(sizeBox/6);
        }
    
        popMatrix();
      } // method 
    
      String toString() {
        return 
          str(x) +","+ 
          str(y) +","+ 
          str(z) +","+ 
          str(rotateY) +","+ 
          str(type) +","+ 
          str(red(colBox)) +","+ 
          str(green(colBox)) +","+ 
          str(blue(colBox)) ;
      }
      //
    } // class 
    //
    
  • there is still a lot to do:

    • technically the variables translatex,translatey,translatez, sizeBox etc. before setup() are not necessary but should be just Box3D placingBox; or so.

    • rotation of one block is not supported

    • no color selector

    • no textures for boxes

    • no save and load scenes

    • no PLAY mode where you can just look at the scene (run through it) without editing it

    Chrisir ;-)

  • Thank you! This has solved my problem. Also, here is the code, modified to suit my needs:

    import peasy.PeasyCam;
    
    PeasyCam cam;
    
    final float gravity = 1; //Inaccurate gravity simulation (impulse rather than force)
    
    // ArrayLists for the class
    ArrayList<Box3D> boxes = new ArrayList(); 
    
    // this is a very important size: size boxes
    float sizeBox = 70.0;
    float sizeLimit = 180;
    
    float rotationX;
    float rotationY;
    float rotationZ;
    
    float translatex=-sizeBox;
    float translatey=-sizeBox;
    float translatez=0;
    
    float z = 1.0;
    
    int objectNumber = 0;
    
    // folded triangle 
    PShape s; 
    
    float offsetZ=400;
    
    color colCurrent=color(0, 0, 255);
    
    boolean showHelpText=true; 
    
    void setup() {
    
      size(500, 500, P3D);
      background(0);
      sphereDetail(32);
    
      cam = new PeasyCam(this, 400); // new 
    
      s = createShape();
      s.beginShape();
      s.fill(255, 0, 0);
      s.stroke(177);
      s.vertex(100, 0, -100);
      s.vertex(-60, 0, 200);
      s.vertex(-50, 60, -30);
      s.vertex(110, 160, 40);
      s.scale(0.4);
      s.endShape(CLOSE);
    }
    
    void draw() {
      background(0);
      lights();
    
      for (Box3D b : boxes ) {
        b.display();
      }
    
      showObject();
    
      cam.beginHUD(); 
      if (showHelpText) {
        fill(255); // white 
        text ("Use PeasyCam. Use wasd and pl; r/g/b for colors, space bar to toggle object, RETURN to enter object to list.\n\n"
          +"Use x to switch this text on and off. \n"
          +"Use Mousewheel to zoom, double click to reset view. The box you move has a white outline. An item that has been added to the list is marked with a gray box.\n"
          +"On adding to the list the placing box moves two units left. \n"
          +"Current: "
          +translatex
          +", "
          +translatey
          +", "
          +translatez, 
          19, 19);
      }
    
      if (keyPressed)
        keyPressed_ForContinuusKeyinput();
    
      cam.endHUD();
    }
    
    // -----------------------------------------------------------
    
    void showObject() {
    
      // show object 
    
      pushMatrix();
    
      translatey+=gravity;
    
      translatex=constrain(translatex,-sizeLimit,sizeLimit);
      translatey=constrain(translatey,-sizeLimit,sizeLimit);
      translatez=constrain(translatez,-sizeLimit,sizeLimit);
    
      fill(colCurrent); // color
      //translate(width/3, height/2, 0);
      translate(translatex, translatey, translatez);
      //rotateZ(rotationZ);
      //rotateY(rotationY);
      //rotateX(rotationX);
    
      stroke(255); 
    
      if (objectNumber==0) {
        //stroke(111);
        box(sizeBox);
      } else if (objectNumber==1) {
        s.setFill(colCurrent);
        shape(s, 0, 0);
      } else if (objectNumber==2) {
        //noStroke();
        sphere(sizeBox);
      }//else 
    
      popMatrix();
    }
    
    // -----------------------------------------------------------
    
    void keyPressed_ForContinuusKeyinput() {
      // keyPressed for registering a key throughout  
    
      float speed = 3.9; 
    
      switch(key) {
    
      case 'a':
        translatex-=speed;
        break; 
    
      case 'd':
        translatex+=speed;
        break;
    
      case 'w':
      case ' ':
        translatey-=speed; //Conflicting keypress info: change object
        break;
    
      case SHIFT:
      case 's':
        translatey+=speed;
        break;
    
        // --
    
      case 'p':
        translatez-=speed;
        break; 
    
      case 'l':
        translatez+=speed;
        break; //Unnecessary in 2D version, but useful
    
        // -----
    
        //
      }//switch
      //
    }//func
    
    // ----------------------------
    
    void keyPressed() {
    
      // keyPressed for registering a single key only 
    
      switch(key) {
        /*
      case ' ':
        objectNumber++;
        if (objectNumber>2)
          objectNumber=0;
        key=0;
        break;
        */ //Object switch disabled in favor of jumping
    
      case RETURN:
      case ENTER:
        Box3D newBox = new Box3D (
          translatex, 
          translatey, 
          translatez, 
          rotationY, 
          objectNumber, 
          colCurrent
          ); 
        boxes.add(newBox); 
        translatex-=sizeBox*2;
        break;
    
        // ---
        //colors
    
      case 'r':
        colCurrent=color(255, 0, 0);
        break;
    
      case 'b':
        colCurrent=color(0, 0, 255);
        break;
    
      case 'g':
        colCurrent=color(0, 255, 0);
        break;
    
        // --
    
      case 'x':
        showHelpText = !showHelpText; 
        break;
      }//switch
      //
    } //func
    
    //=============================================================
    
    class Box3D {
    
      float x=mouseX, y=410, z=mouseY;
    
      float offsetX=0; 
      float offsetZ2=0; 
    
      float rotateY=0;
      color colBox = color(255, 0, 0);
    
      // type
      final int boxTypeUndefined = -1;
      final int boxType0   = 0;
      final int boxType1   = 1; 
      final int boxType2   = 2; 
      // more needed !!!!! ???? 
      int type = boxTypeUndefined; 
    
      // constr - full
      Box3D(float x_, 
        float y_, 
        float z_, 
        float rotateY_, 
        int type_, 
        color col1_) {
        //
    
        x=x_;
        y=y_;
        z=z_;
    
        rotateY= rotateY_;
        type=type_;
    
        colBox=col1_;
      } // constr 2 
    
      void display() {
        pushMatrix();
    
        translate(x, y, z);
        //  rotateY(radians(rotateY)); 
    
        fill(colBox);
        noStroke(); 
    
        // depending on the current buttons tag 
        // 
        switch (type) {
    
        case boxTypeUndefined:
          // undefined 
          break; 
    
        case boxType0: 
          stroke(111);
          box(sizeBox);
          break; 
    
        case boxType1: 
          s.setFill(colBox);
          shape(s, 0, 0);
          break;
    
        case boxType2: 
          noStroke();
          sphere(sizeBox);
          break;
    
        default:
          // error 
          break;
        }//switch 
    
        if (showHelpText) {
          translate(sizeBox/2, -sizeBox/2, sizeBox/2);
    
          fill(111);
          stroke(0);
          box(sizeBox/6);
        }
    
        popMatrix();
      } // method 
    
      String toString() {
        return
          str(x) +","+ 
          str(y) +","+ 
          str(z) +","+ 
          str(rotateY) +","+ 
          str(type) +","+ 
          str(red(colBox)) +","+ 
          str(green(colBox)) +","+ 
          str(blue(colBox)) ;
      }
      //
    } // class 
    //
    
  • This thread has been closed, in favor of QueasyCam. You can now stop commenting.

    P.S. I looked at the source code, and it uses camera(). I copied down the source code, and embedded it in my own script. Here it is, for a TerraTech clone instead of a Minecraft clone:

    // Variables
    String KEY_MOVE_FORWARD_1 = 'w';
    String KEY_MOVE_FORWARD_2 = 'W';
    String KEY_MOVE_LEFT_1 = 'a';
    String KEY_MOVE_LEFT_2 = 'A';
    String KEY_MOVE_BACK_1 = 's';
    String KEY_MOVE_BACK_2 = 'S';
    String KEY_MOVE_RIGHT_1 = 'd';
    String KEY_MOVE_RIGHT_2 = 'D';
    String KEY_MOVE_UP_1 = 'q';
    String KEY_MOVE_UP_2 = 'Q';
    String KEY_MOVE_DOWN_1 = 'e';
    String KEY_MOVE_DOWN_1 = 'E';
    String KEY_JUMP = ' ';
    
    boolean debugMode = true;
    float friction = 0.8;
    int debugSetting = 0;
    
    PVector mouse = new PVector();
    PVector prevMouse = new PVector();
    Robot robot = new Robot();
    
    PVector forward = new PVector();
    PVector right = new PVector();
    PVector up = new PVector(0, 1, 0);
    PVector position = new PVector();
    PVector velocity = new PVector();
    PVector center = new PVector();
    
    void newGame() {
    
      // Generate noise map
      // Introduction sequence
      // Tutorial sequence, if enabled in new game menu
    }
    
    void setup() {
    
      // Title screen
      // Menu (buttons, checkboxes, random seed, etc.)
    }
    
    void draw() {
    
      // Mouse looping
      if (mouse.x < 1 && (mouse.x - prevMouse.x) < 0) {
        robot.mouseMove(width - 2, mouse.y);
        mouse.x = width - 2;
        prevMouse.x = width - 2;
      }
      if (mouse.x > width - 2 && (mouse.x - prevMouse.x) > 0) {
        robot.mouseMove(2, mouse.y);
        mouse.x = 2;
        prevMouse.x = 2;
      }
      if (mouse.y < 1 && (mouse.y - prevMouse.y) < 0) {
        robot.mouseMove(mouse.x, height - 2);
        mouse.y = height - 2;
        prevMouse.y = height - 2;
      }
      if (mouse.y > height - 1 && (mouse.y - prevMouse.y) > 0) {
        robot.mouseMove(mouse.x, 2);
        mouse.y = height - 2;
        prevMouse.y = 2;
      }
    
      // Pan and tilt commands
      pan += map(mouse.x - prevMouse.x, 0, width, 0, TAU) * sensitivity;
      tilt += map(mouse.y - prevMouse.y, 0, height, 0, PI) * sensitivity;
      tilt = constrain(tilt, -PI/2.01, PI/2.01);
      if (tilt == PI/2) tilt += 0.001;
    
      // Forward and right commands
      forward.x = cos(pan);
      forward.y = tan(tilt);
      forward.z = sin(pan);
      forward.normalize();
    
      right.x = sin(pan);
      right.y = 0;
      right.z = -cos(pan);
    
      // Controls
      prevMouse.x = mouse.x;
      prevMouse.y = mouse.y;
    
      if (keyPressed) {
        switch(key) {
    
          case KEY_MOVE_FORWARD_1:
          case KEY_MOVE_FORWARD_2:
            velocity.add(PVector.mult(forward, speed));
          break;
    
          case KEY_MOVE_LEFT_1:
          case KEY_MOVE_RIGHT_1:
            velocity.add(PVector.mult(right, speed));
          break;
    
          case KEY_MOVE_BACK_1:
          case KEY_MOVE_BACK_2:
            velocity.sub(PVector.mult(forward, speed));
          break;
    
          case KEY_MOVE_RIGHT_1:
          case KEY_MOVE_RIGHT_2:
            velocity.sub(PVector.mult(right, speed));
          break;
    
          case KEY_JUMP:
            velocity.add(PVector.mult(up, speed));
          break;
        }
    
        if (debugMode) {
          switch(key) {
    
            case KEY_MOVE_UP_1:
            case KEY_MOVE_UP_2:
              velocity.add(PVector.mult(up, speed));
            break;
    
            case KEY_MOVE_DOWN_1:
            case KEY_MOVE_DOWN_2:
              velocity.sub(PVector.mult(up, speed));
            break;
          }
        }
      }
    
      // Movement and camera
    
      velocity.mult(friction);
      position.add(velocity);
      center = PVector.add(position, forward);
      camera(position.x, position.y, position.z,
             center.x,   center.y,   center.z,
             up.x,       up.y,       up.z);
    
      // Actual graphics
      if (debugMode) {
        switch(debugSetting) {
    
          case 0:
    
            // Wavy cubes
    
          break;
    
          case 1:
    
            // Grid
          break;
        }
        // Draw cube
      } else {
        // Draw tech(s)
        // Draw terrain, generated in noise map
      }
    }
    
  • Also, I forgot to include these lines at the very beginning, for the robot:

    import java.awt.*;
    import java.awt.event.*;
    
  • Thank you!

  • You're welcome!

Sign In or Register to comment.