Using keyPressed() events within objects - is this 'done'?

edited January 2015 in Questions about Code

I've just been trying to make a basic pong game. I have the paddle working and now I'm trying to create a 'bat' object for this.

Display and that is cool, but I was thinking that I'd be able to copy paste the keyPressed() and keyReleased() event functions into the objects method area, I'm not sure whether this is possible or not. Do they have to be global? As in, outside all other { } braces?

The bottom bat works as expected, the top one doesn't. The top one is the object, bottom one the one declared in draw(). I'm not sure how to handle this, here's the code :

Bat bat = new Bat();

float directionX = 1;
float x = 100;
boolean lefting, righting;

void setup() {
  size(700, 500);
  background(0);
}

void draw() {
  background(0);

  if (lefting) {
    directionX -= 7.3;
  }
  if (righting) {
    directionX += 7.3;
  }
  x += directionX;

  rect(x, height-50, 100, 40);

  if ((x>width-100) || (x<0)) {
    directionX *=-1;
  }
  directionX = 0;
  bat.display();
  bat.move();
}

void keyPressed() {
  if (key==CODED) {
    if (keyCode==LEFT) {
      lefting=true;
    } else if (keyCode==RIGHT) {
      righting = true;
    }
  }
}

void keyReleased() {
  if (key==CODED) {
    if (keyCode==LEFT) {
      lefting = false;
    } else if (keyCode==RIGHT) {
      righting=false;
    }
  }
}

class Bat {

  float batDirectionX;
  float batX;
  boolean batLefting, batRighting;


  Bat() {
    batDirectionX = 1;
    batX = 100;
  }

  void display() {
    rect(batX, 100, 100, 100);
  }

  void move() {
    if (lefting) {
      batDirectionX -= 7.3;
    }
    if (righting) {
      batDirectionX += 7.3;
    }
    batX += batDirectionX;
  }

  void keyPressed() {
    if (key==CODED) {
      if (keyCode==LEFT) {
        batLefting=true;
      } else if (keyCode==RIGHT) {
        batRighting = true;
      }
    }
  }

  void keyReleased() {
    if (key==CODED) {
      if (keyCode==LEFT) {
        batLefting = false;
      } else if (keyCode==RIGHT) {
        batRighting=false;
      }
    }
  }
}

Answers

  • edited January 2015

    void keyPressed() outside the class is called automatically if a key is pressed;

    inside the class it is not.

    (so you can call your keyPressed() that is inside the class from the one that is outside of it.)

    or you just stick with the keyPressed() outside the class and say

    bat.batLefting = true;
    

    here.

    So it's object - then the dot - then the var or method that's inside the class, see?

    This

    float directionX = 1;
    float x = 100;
    boolean lefting, righting;
    

    shouldn't be before setup(), you have it all in the class.

    just delete it before setup()

    The stuff you have in draw() belongs inside the class in a new function called script or inside move or so

     if (lefting) {
        directionX -= 7.3;
      }
      if (righting) {
        directionX += 7.3;
      }
      x += directionX;
    
      rect(x, height-50, 100, 40);
    
      if ((x>width-100) || (x<0)) {
        directionX *=-1;
      }
      directionX = 0;
    

    (where you have it already; when using a class, do it completely in the class)

    see

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

    ;-)

  • Cheers Chris - there are two rectangles at the moment.

    Where you mention about using the dot to access things, I thought about doing something along those lines but wasn't sure if that would be messy as it wasn't included in the actual object then? Like, not all of the objects functionality would be in the object if it was dependent on being triggered by the keyPressed() method from outside, if that makes sense?

  • edited January 2015

    Hey chris, that's for your help (again!)

    I have the following code, but I'm not sure it really feels right :

    Paddle g_pad = new Paddle();
    
    void setup() {
      size(700, 500);
      background(0);
    }
    
    void draw() {
      background(0);
      g_pad.doPaddle();
    }
    
    void keyPressed() {
      if (key==CODED) {
        if (keyCode==LEFT) {
          g_pad.lefting = true;
        } else if (keyCode==RIGHT) {
          g_pad.righting = true;
        }
      }
    }
    
    void keyReleased() {
      if (key==CODED) {
        if (keyCode==LEFT) {
          g_pad.lefting =false;
        } else if (keyCode==RIGHT) {
          g_pad.righting = false;
        }
      }
    }
    
    
    class Paddle {
      float directionX = 1; // this is the movement of the paddle
      float x = 100; // the initial starting position of the paddle
      boolean lefting, righting;  
    
      Paddle( ) {
      }
    
      void doPaddle() {
    
        rect(x, height-50, 100, 40);
        if (lefting) {
          directionX -= 7.5;
        }
        if (righting) {
          directionX += 7.5;
        }
    
        x += directionX;
    
        if ((x>width-100) || (x<0)) {
          directionX *=-1;
          //  directionX = 0.1;
        }
        directionX = 0;
      }
    }
    
  • looks great!

  • Actually it is possible to make objects respond to key events. It uses the registerMethod which is used to make objects listen for events. The technique is used frequently in contributed libraries.

    If you run the sketch below you see two red balls (Foo objects). The one on the left uses the keys 1 & 2 to move up and down, the one on the right uses the 8 & 9 keys for the same thing.

    Notice that the class Foo must be declared static so it becomes a top level class. The object is registered in its constructor and the keyEvent method listens for key events.

    Foo f0, f1;
    
    public void setup() {
      size(200, 200);
      f0 = new Foo(this, 50, 100, '1', '2');
      f1 = new Foo(this, 150, 100, '8', '9');
    }
    
    public void draw() {
      background(96);
      f0.render();
      f1.render();
    }
    
    static public class Foo {
      PApplet app;
    
      int x, y, s;
      char upKey, downKey;
    
      Foo(PApplet papp, int px, int py, char up, char down) {
        app = papp;
        x = px;
        y = py;
        s = 20;
        upKey = up;
        downKey = down;
        app.registerMethod("keyEvent", this);
      }
    
      public void render() {
        app.noStroke();
        app.fill(255, 0, 0);
        app.ellipse(x, y, s, s);
      }
    
      public void keyEvent(KeyEvent event) {
        if (event.getKey() == upKey)
          y -= 2;
        else if (event.getKey() == downKey)
          y += 2;
      }
    }
    
  • Hey Quark, I've not actually seen any of this in the tutorials or the Learn Processing book that I've been going through.

    I'm guessing that these elements are more Java related than Processing? (But then processing is Java, so maybe that's a daft comment...)

    Things that I'm unfamiliar with :

    line 3,9 : using the 'public' keyword

    line 15 : static

    line 16 : PApplet app;

    line 5,6,28 : 'this' keyword

    Bearing the above in mind, would you suggest that I utilise the method you've suggested or that it is too advanced? Also ,are there any learning resources that you would suggest? I'm currently going through Learning Processing.

    Thanks both.

  • Bearing the above in mind, would you suggest that I utilise the method

    This is fairly advanced stuff and since you have to ask I suggest that you don't try to incorporate this into your program yet. The public and static keywords are Java keywords and related to object orientated programming. PApplet is a class in Processing, the registerMethod is a method of the PApplet class.

    It would be something to come back to and experiment with when you are more familiar with OO principles and syntax.

  • edited January 2015

    OK no worries. I always think it's nice to be made 'aware' of these things prior to one using them anyway so thanks for introducing them to me :)

    I still feel that my comment above was a bit off key, though perhaps it's acceptable for the level I'm at (I'm happy to leave it as is if thats the case)

    cheers

  • edited January 2015
    • PDE (Processing's IDE) got a pre-processor which converts some Processing peculiarities into real Java!
    • Check that out by exporting the sketch as app (CTRL+E) and looking for the generated ".java" file there.
    • Most prominent things we'll see is public access keyword automatically slapped in almost anything.
    • Well, except classes & methods from locally anonymous instantiations.
    • Thus, in the point of view of Processing framework, public keyword is mostly redundant and Java-ish!
    • Another thorn in Processing is nested static classes.
    • A class declared so can't access graphics related API w/o the PApplet reference from the top sketch.
    • You can see that within @quark's render() method using app field for all drawing calls.

    I've re-written @quark's example and made some mods:

    • Removed static declaration from the class.
    • Removed all redundant public declarations. Except for the class, since it's not auto-added.
    • Sadly access used by registerMethod() calls demand both class & its method to be both public!
    • Removed PApplet parameter from class' constructor.
    • In its place, it forcibly founds that out by itself via a getEnclosingPApplet() call.

    // forum.processing.org/two/discussion/9022/
    // using-keypressed-events-within-objects-is-this-039done039
    
    InnerClass inner1, inner2;
    
    void setup() {
      size(200, 200, JAVA2D);
      smooth(4);
      frameRate(30);
    
      noStroke();
      fill(InnerClass.INK);
      ellipseMode(CENTER);
    
      inner1 = new InnerClass(width>>2, height>>1, '1', '2');
      inner2 = new InnerClass(3*width>>2, height>>1, '8', '9');
    }
    
    void draw() {
      background(0150);
      inner1.render();
      inner2.render();
    }
    
    public class InnerClass {
      static final color INK = #FF0000;
      static final short SPD = 2, DIAM = 20, RAD = DIAM>>1;
    
      short x, y;
      char  up, down;
    
      InnerClass(int px, int py, char north, char south) {
        x = (short) px;
        y = (short) py;
    
        up = north;
        down = south;
    
        getEnclosingPApplet().registerMethod("keyEvent", this);
      }
    
      void render() {
        ellipse(x, y, DIAM, DIAM);
      }
    
      void keyEvent(KeyEvent e) {
        int k = e.getKey();
        int move = SPD * (int(k == down) - int(k == up));
        y = (short) constrain(y + move, RAD, height - RAD);
      }
    
      protected PApplet getEnclosingPApplet() {
        try {
          return (PApplet) getClass()
            .getDeclaredField("this$0").get(this);
        }
        catch (ReflectiveOperationException cause) {
          throw new RuntimeException(cause);
        }
      }
    }
    
  • edited January 2015

    Notice that the class Foo must be declared static so it becomes a "top level class".

    Again that word MUST in the context of programming "truths"... : [-(

    • Declaring a nested class static doesn't transform it into a "top level class"!
    • It's true it behaves very much like 1. That is so b/c all "top level classes" are indeed static!
    • Still there are some minutiæ: Each ".java" file allows only 1 public "top level class" in it.
    • However, we can have as many public static nested classes in it! <:-P

    • Also as demonstrated in my modified example, there are 2 conditions for registerMethod().
    • Both class & registered method needs to be public.
    • registerMethod() needs to be invoked over a PApplet reference.
    • However, the class itself doesn't need to be static! :-@
  • edited January 2015

    Yes to be more accurate I should have written

    Notice that the class Foo must be declared static so it behaves like a top level class.

    If course it would be simple to make the Foo class top level, create a new tab called Foo.java, move the class code into this tab, remove the static keyword and add an import for processing.core.*

    Anyway there is nothing quite like a nice bit of pedantry :D

  • Thanks both, I feel this is all quite far out from my current level though.

  • I feel this is all quite far out from my current level though.

    Sorry for the advanced stuff. All you need to remember is that it can be done and come back to this when you feel ready. :)

  • Answer ✓

    until then use my approach; your own code was good

  • @Quark

    Sorry for the advanced stuff. All you need to remember is that it can be done and come back to this when you feel ready

    No problem, the only issue with having advanced stuff like this presented is when there's ambiguity as to whether or not I should understand / utilise it currently. That hasn't been the case here so thanks for the intro :)

    @Chrisir

    Fair, I guess my intuition about the code being wrong was off then (not a massive surprise really!)

    cheers all :)

  • Answer ✓

    great ;-)

Sign In or Register to comment.