Smooth Moving code


#1

I’m making a game separate from this specific code I just can’t get right… It’s for using the arrow keys to move around smoothly. But there seems to be a bad bug happening.

float x = 100; 
float y = 100; 

void setup() {
  size(600, 400);
}

void draw() {
  background(0);

  rect(x, y, 10, 10);
}

void keyPressed() {
  if (key == CODED) {
    if (keyCode == UP) {
      y = y - 5;
    }
    if (keyCode == DOWN) {
      y = y + 5;
    }
    if (keyCode == LEFT) {
      x = x - 5;
    }
    if (keyCode == RIGHT) {
      x = x + 5;
    }
  }
}

That’s the fastest code I can make as a moving square prototype. In this specific case, the square moves 5 pixels, then waits a fraction of a second to continue moving. In my actual game code, my square moves infinitely in the last direction specified by arrows. I also can’t move diagonally at all cuz one key was pressed first.

Now I added the calling of keyPressed in draw, and the exact same thing happened that I described

float x = 100; 
float y = 100; 

void setup() {
  size(600, 400);
}

void draw() {
  background(0);

  rect(x, y, 10, 10);

  keyPressed();
}

void keyPressed() {
  if (key == CODED) {
    if (keyCode == UP) {
      y = y - 5;
    }
    if (keyCode == DOWN) {
      y = y + 5;
    }
    if (keyCode == LEFT) {
      x = x - 5;
    }
    if (keyCode == RIGHT) {
      x = x + 5;
    }
  }
}

Is there any way possible to make motion fluid? If a key is pressed at all it simply INFLUENCES the notion of movement towards a final moving speed.
If you press LEFT and RIGHT at the same time (or at any given point in time that they r both being pressed simultaneously) then speed overall is 0. The same principle for LEFT and UP at the same time, Y is influenced the same amount that X is influenced and you go diagonally.
I’m gonna end up experimenting and finding the answer soon cuz I find that when I explain things in forums I accidentally actually figure the answer out. I’ll comment here if I do.


#2

I have 2 suggestions - first is to make 4 boolean variables storing the state of arrow keys, turning them true in keyPressed() and false in keyRelased().

Then, you can add 2 separate values for X and Y velocity, and, based on these 4 boolean, add to or subtract from them, and, in draw(), add them to position values.

I’m going to post the code doing just that in a second.


#3

Studio.ProcessingTogether.com/sp/pad/export/ro.91tcpPtI9LrXp


#4

Here, your code adjusted to move the square around smoothly.

float x = 100; 
float y = 100; 

float xSpeed = 0;
float ySpeed = 0;

boolean keyLeft, keyRight, keyUp, keyDown;

void setup() {
  size(600, 400);
}

void draw() {
  background(0);

  rect(x, y, 10, 10);
  
  countSpeed();   //Change speed based on current keys pressed.
  changePosition(); //Change position based on speed.
  
  xSpeed *= 0.9; //Apply some drag so that the square wouldn't fly off the screen indefinitely
  ySpeed *= 0.9;
  
}


void countSpeed(){
  if(keyLeft) xSpeed-= 0.5;
  if(keyRight) xSpeed+= 0.5;
  if(keyUp) ySpeed-= 0.5;
  if(keyDown) ySpeed+= 0.5;
}

void changePosition(){
  x+=xSpeed;
  y+=ySpeed;
}




void keyPressed() {
//  if (key == CODED) {
    if (keyCode == UP) {
      keyUp = true;
    }
    if (keyCode == DOWN) {
      keyDown = true;
    }
    if (keyCode == LEFT) {
      keyLeft = true;
    }
    if (keyCode == RIGHT) {
      keyRight = true;
    }
//  }
}
void keyReleased() {
//  if (key == CODED) {
    if (keyCode == UP) {
      keyUp = false;
    }
    if (keyCode == DOWN) {
      keyDown = false;
    }
    if (keyCode == LEFT) {
      keyLeft = false;
    }
    if (keyCode == RIGHT) {
      keyRight = false;
    }
//  }
}

#5

I’ve been looking at the link posted by GoToLoop (cuz thats the code that works best with what I’m trying to do) and I deciphered most of the code. I am having trouble understanding, syntax wise, a few parts. Conceptually I get whats happening, but I need to be lightly walked through what actually going on in some parts.

static final int DIAM = 48, SPD = 4, FPS = 60;
static final color BG = 0350;
 
Player p;
 
void setup() {
  size(800, 600, JAVA2D);
 
  smooth(4);
  frameRate(FPS);
  ellipseMode(CENTER);
 
  fill(Player.INK);
  stroke(Player.OUTLINE);
  strokeWeight(Player.BOLD);
 
  p = new Player(width>>1, height>>1, DIAM, SPD);
}
 
void draw() {
  background(BG);
  p.move();
  p.display();
}
 
void keyPressed() {
  p.setMove(keyCode, true);
}
 
void keyReleased() {
  p.setMove(keyCode, false);
}
 
final class Player {
  static final color INK = #008000, OUTLINE = 0;
  static final float BOLD = 2.0;
 
  boolean isLeft, isRight, isUp, isDown;
  int x, y;
  final int d, v;
 
  Player(int xx, int yy, int dd, int vv) {
    x = xx;
    y = yy;
    d = dd;
    v = vv;
  }
 
  void display() {
    ellipse(x, y, d, d);
  }
 
  void move() {
    x = constrain(x + v*(int(isRight) - int(isLeft)), d>>1, width  - (d>>1));
    y = constrain(y + v*(int(isDown)  - int(isUp)),   d>>1, height - (d>>1));
  }
 
  boolean setMove(int k, boolean b) {
    switch (k) {
    case 'W':
    case UP:
      return isUp = b;
 
    case 'S':
    case DOWN:
      return isDown = b;
 
    case 'A':
    case LEFT:
      return isLeft = b;
 
    case 'D':
    case RIGHT:
      return isRight = b;
 
    default:
      return b;
    }
  }
}

declaring a new “p = new Player(width>>1, height>>1, DIAM, SPD);” I get the DIAM (diameter) and SPD (speed), but whats width>>1 and height>>1 mean? Google said something abt shifting, and based on what happens in the code, they mean the half of width and height.

void move() {
    x = constrain(x + v*(int(isRight) - int(isLeft)), d>>1, width  - (d>>1));
    y = constrain(y + v*(int(isDown)  - int(isUp)),   d>>1, height - (d>>1));
  }

I assume this means no going beyond 0 < x < width and 0 < y < height. Constrain means returning a value such that (a, b, c) A doesn’t go below B or above C. Is there a different way to write that in general? If I c it written another way it might spark an idea or understanding.

Beyond that I may have further questions about how to implement those methods into my main code later on. I have an array of “level” arrays, and each one contains all the items for the level (from various classes). I know how to loop through them to “draw” all the walls, keys, enemies, and the player at once, but removing all “level1” items then immediately drawing “level2” will be a difficulty.

Thanks for all your replies I appreciate it!


#6

Yes, >> and << are bit-shifting operations. Doing number<<1 shifts bits one to the left, effectively doubling the number - a little bit faster alternative to number*2. Doing number>>1 shifts bits one to the right , effectively halving the number - a faster alternative to number/2.
It’s not applicable to floats and doubles though, as you can’t just bit-shift these to halve/double them, unlike with bytes, shorts, integers, longs etc…

And yes, constrain(a,b,c) function could be completely replaced with min(max(a,b),c). So, his code could be understood easier as:

void move() {
    x = min(max(x + v*(int(isRight) - int(isLeft)), d>>1)  , width  - (d>>1));
    y = min(max(y + v*(int(isDown)  - int(isUp)),   d>>1)  , height - (d>>1));
  }

It calculates how much x and y should change based on keys, and then clamps it between half the diameter and the other side minus half the diameter, resulting in the Player's edge aligning with the borders of the screen.


#7

All the walls inside my game are inside the width and height, and I added a secondary player class with the code on Studio.ProcessingTogether.com/sp/pad/export/ro.91tcpPtl9LrXp and implemented it as an object and placed different constraints based on more confined walls and it worked.

x = constrain(x + v*(int(isRight) - int(isLeft)), (d>>1)+42, width - (d>>1)-151);
y = constrain(y + v*(int(isDown) - int(isUp)), (d>>1)+62, height - (d>>1)-41);

Now that is the code for predetermined walls. If I were to make a “colliding with wall” function which takes in any universal wall and applies logic from http://www.jeffreythompson.org/collision-detection/circle-rect.php , where would I place it? Inside the move function? or a separate float method which returns x*-1 or y*-1 if I hit a wall with my circle player (the player will eventually be somewhat of a character sprite but for now it’s a simple circle.

Side note I thought I would eventually mention this but there’s a problem in processing where an object won’t move at all if pressing any button. Whether it moves at all seems random (like there’s a glitch switching between “ye I’ll move” and “nope”) and the trigger to making something work is literally erasing and adding back a random piece of code. like backspacing on a “;” then adding it back, saving, and running and it’ll work.


#8

I think you could put the collision code in a separate from move() function that is run exactly after move(), something like collide(), inside of that same Player class.
Then, you could make another class Wall that stores all of the needed wall coordinates, and pass the Wall[] array to collide() function. Then, you could use a for to iterate through all walls.

void collide(Wall[] inWalls){
  for(Wall i:inWalls){
    //Your collision code . . .
  }
}

#9

I created 2 functions inside the player class. The first is right after the other move function, and the other decides which side of a wall im closest to. Now I have a “moveOutsideWalls()” (which keeps player inside the 4 universal walls every map has) and “moveInsideWalls()” which at most times will override the outside walls cuz it adds more constraint on player’s movement.

The way I thought this through was this:

function which returns an int of which side im closest to:

int RectSide(Wall wall) {
    int x = 0;
    int y = 0;
    if (this.y+r > wall.y && this.y+r < wall.y+wall.ySize) {
      if (this.x+r < wall.x) {
        x = 1;
      } else if (this.x+r > wall.x+wall.xSize) {
        x = 2;
      }
    }
    if (this.x > wall.x && this.x < wall.x+wall.xSize) {
      if (this.y+r < wall.y) {
        x = 3;
      } else if (this.y+r > wall.x + wall.xSize) {
        x = 4;
      }
    }

then based on which side we r next to, a method which places custom constraints on x and y movement.

void moveInsideWalls(int q, Wall wall) {
    if (q == 1) {
      x = constrain(x + v*(int(isRight) - int(isLeft)), (d>>1)+42, wall.x);
      y = constrain(y + v*(int(isDown)  - int(isUp)), (d>>1)+62, height - (d>>1)-41);
    }
    if (q == 2) {
      x = constrain(x + v*(int(isRight) - int(isLeft)), wall.x + wall.xSize, (d>>1)-151);
      y = constrain(y + v*(int(isDown)  - int(isUp)), (d>>1)+62, height - (d>>1)-41);
    }
    if (q == 3) {
      x = constrain(x + v*(int(isRight) - int(isLeft)), (d>>1)+42, width  - (d>>1)-151);
      y = constrain(y + v*(int(isDown)  - int(isUp)), wall.y + wall.ySize, (d>>1)-41);
    }
    if (q == 4) {
      x = constrain(x + v*(int(isRight) - int(isLeft)), (d>>1)+42, width  - (d>>1)-151);
      y = constrain(y + v*(int(isDown)  - int(isUp)), (d>>1)+62, wall.y);
    }
  }

both functions r referenced in the main code area like this.

for (int i = 0; i <= level1.length-1; i++) {
    level1[i].draw();
    player2[0].moveOutsideWalls();
    if (player2[0].RectSide(walls[i]) == 1) {
      player2[0].moveInsideWalls(1, walls[i]);
      //println(player2[0].CollisionWithRect(walls[i]));
    }
    if (player2[0].RectSide(walls[i]) == 2) {
      player2[0].moveInsideWalls(2, walls[i]);
    }
    if (player2[0].RectSide(walls[i]) == 4) {
      player2[0].moveInsideWalls(3, walls[i]);
    }
    if (player2[0].RectSide(walls[i]) == 3) {
      player2[0].moveInsideWalls(4, walls[i]);
    }
  }

The method I initially used to create the levels might’ve been odd, cuz I’m brand new to making games using actual code (the original game was made in scratch, and I’m challenging myself to use processing).

I created a wall class and each wall has x, y, xSize, ySize.

In my main area, building the levels individually took time, but I basically created an array of size 100, and walls 0-3 was level 1, walls 4-8 is level 2 and so on. after creating the levels I added them (along with other things that a level would have) into 1 simple level1 array, level2 array so on. Looping through level1 and drawing all the level1 items is easy and works. Need to add “extends level” for every class that’ll be included in a level[i] array.

So I guess my question is why does the above movement logic not work? Despite my explicit player object being placed at a specific x, y, the first frame shows player inside a wall … (my wall class is set to show every wall as fill(255) so they appear as frames for now, and the player is inside the frame)
Another weird thing that happens which could be studied to solve what’s going on is IF the player ends up moving at all, reaching the top right corner of the wall (aka the outer walls) it’ll stay there. but if you get to the right side of the wall (which still inside) it immediately places you back at x=0. reaching about 70% down the y-axis of the wall results in y=0. I can post pics of what’s going on if need b.


#10

As you probably have guessed already, collision, even simple, is a pretty hard and confusing topic.

I once dabbled with it myself, and came up with a solution to simple 2D rectangle collision. I have no idea how to translate it to balls, so, with my solution, your player is now a square - or any rectangle, if you wish.
I’ve modified your sketch, removed all the constraint things, and made collisions.

static final int DIAM = 48, SPD = 4, FPS = 60;
static final color BG = 0350;
 
Player p;
ArrayList<Wall> walls;
void setup() {
  size(800, 600, JAVA2D);
 
  smooth(4);
  frameRate(FPS);
  ellipseMode(CENTER);
 
  fill(Player.INK);
  stroke(Player.OUTLINE);
  strokeWeight(Player.BOLD);
  p = new Player(width>>1, height>>1, DIAM, DIAM, SPD);
  
  walls = new ArrayList<Wall>();
  //Off-screen screen borders. May cause black outlines because Processing tries to draw them as they are just off the screen.
  walls.add(new Wall(-50,-50,width,50));
  walls.add(new Wall(-50,-50,50,height+100));
  walls.add(new Wall(width,-50,50,height+100));
  walls.add(new Wall(-50,height,width+100,50));
  
  //Any old walls.
  walls.add(new Wall(100,100,200,100));
  walls.add(new Wall(200,150,100,100));
}
 
void draw() {
  background(BG);
  p.move();
  p.display();
  for(Wall i:walls) i.render();
  
}
 int pos(int x,int y){
  return (y)*width+x;
}
void keyPressed() {
  p.setMove(keyCode, true);
}
 
void keyReleased() {
  p.setMove(keyCode, false);
}
 
 class Wall{
   int x, y, sizex, sizey;
   Wall(int x, int y,int sizex, int sizey){
     this.x = x; this.y = y; this.sizex = sizex; this.sizey = sizey;
   }
   void render(){
     rect(x,y,sizex,sizey);
   }
 }
 
 
 int minSignless(int a1, int a2){
  //Figures which number is closer to zero.
  return abs(a1)-abs(a2)>0?a2:a1;
}
 
final class Player {
  static final color INK = #008000, OUTLINE = 0;
  static final float BOLD = 2.0;
 
  boolean isLeft, isRight, isUp, isDown;
  int x, y;
  final int sizex, sizey, v;
 
  Player(int xx, int yy, int xs, int ys, int vv) {
    x = xx;
    y = yy;
    sizex = xs;
    sizey = ys;
    v = vv;
  }
 
  void display() {
    rect(x, y, sizex, sizey);
  }
  
  void move() {
    int movementX = v*(int(isRight) - int(isLeft));
    int movementY = v*(int(isDown)  - int(isUp));
    for(Wall i:walls){
//        if(movementX == 0 && movementY == 0) break; //If we aren't moving, break out of the for loop. 
      PVector result = figureCollision(x,y,sizex,sizey,movementX,movementY,i);
      movementX = minSignless(movementX,(int)result.x);
      movementY = minSignless(movementY,(int)result.y);
    }
    x+=movementX;
    y+=movementY;
  }
 
  boolean setMove(int k, boolean b) {
    switch (k) {
    case 'W':
    case UP:
      return isUp = b;
 
    case 'S':
    case DOWN:
      return isDown = b;
 
    case 'A':
    case LEFT:
      return isLeft = b;
 
    case 'D':
    case RIGHT:
      return isRight = b;
 
    default:
      return b;
    }
  }
}

boolean collision(int x, int y, int sizex, int sizey, Wall wall){
  return !(x>=wall.x+wall.sizex||y>=wall.y+wall.sizey||x+sizex<=wall.x||y+sizey<=wall.y);
}
boolean collisionPreciser(int x, int y, int sizex, int sizey, int movx, int movy, Wall wall, int precision){
  //Checks collision "precision" times so that the object won't happen to jump over if it's flying too rapidly fast.
  if(precision<=1) return collision(x+movx,y+movy,sizex,sizey,wall);
  for(int i=1;i<=precision;i++){
    if(collision(x+movx/i,y+movy/i,sizex,sizey,wall)) return true;
  }
  return false;
}

static final int collisionQuality = 2;
//Bump this up if you'll have really thin walls or really fast players - else stuff will fly through.


PVector figureCollision(int x, int y, int sizex, int sizey, int movx, int movy, Wall wall){
  //Figures what movx and movy should be so that it wouldn't collide with this wall.
  
  //Check vertical collision...
  if(collisionPreciser(x,y,sizex,sizey,0, movy,wall,collisionQuality)){
    if(y+sizey/2>wall.y+wall.sizey/2){
      //1 is below the wall...
      return new PVector(movx, (wall.y+wall.sizey)-y);
    }else{
      return new PVector(movx, wall.y-(y+sizey));
    }
  }
  if(collisionPreciser(x,y,sizex,sizey,movx,0,wall,collisionQuality)){
    if(x+sizex/2>wall.x+wall.sizex/2){
      return new PVector((wall.x+wall.sizex)-x,movy);
    }else{
      return new PVector(wall.x-(x+sizex),movy);
    }
  }
  return new PVector(movx,movy);
}

Off-topic: I haven’t slept for the last 26 hours and drank (int)random(6,19) cups of coffee, and this exercise is the thing that finally made me want to go sleep. I’ll probably go sleep now. °¬°