Search code examples
javaprocessinggame-physics2d-games

Why are things in my game shifting when I move? Java Processing environment


I'm creating a 2D game in processing.

  • player is always in center of screen
  • player always faces the mouse
  • Movement is achieved by moving the world relative to the player

Every Game loop:

  • player velocity is added to the position
  • player acceleration is added to velocity
  • acceleration is set to be in direction of the mouse, and proportional to the distance from the mouse to center of the screen.
  • velocity += (-velocity * resistance), so that when you change direction you get a smooth transition.

so basically, you always move towards the mouse with a velocity proportional to the distance from the mouse to player (center of screen).

I have all the mechanics working okay so far, other than one bug with the way objects in my world are being drawn. When I move, the items shift slightly depending on the direction, but they do so smoothly as if it's related to my velocity. it looks very much like a drag effect.

I added a world border, and some random circles in the world. The border seems to stay in the right place, but the circles near the edge can go over the edge if I move in the direction opposite to the wall.

Any ideas what could be causing this? Here is the relevant code (sorry, theres a fair bit..)

import java.util.ArrayList;

//constants
int ROCKET_WIDTH = 32;
int ROCKET_HEIGHT = 52;
float PLAYER_MAX_ACC = 0.4;
float PLAYER_MAX_VEL = 12;
float MOVEMENT_RESISTANCE = 0.05;

//global variables
Rocket player;
ArrayList<PVector> balls;
float offsetX;
float offsetY;

void setup()
{
  size(800,600);
  frameRate(30);
  background(0);
  player = new Rocket();
  balls = new ArrayList<PVector>();
  offsetX = width/2 - player.pos.x;
  offsetY = height/2 - player.pos.y;
  for (int i = 0;i < 100;i++)
  {
    PVector vec = new PVector(random(-width,width*2),random(-height,height*2));
    balls.add(vec);
  }
}

void draw()
{
  background(0);
  drawWorldEdges();
  updateMechanics();
  for (PVector b:balls)
  {
    ellipseMode(CENTER);
    ellipse(b.x + offsetX,b.y + offsetY,10,10);
  }
  drawPlayer();
}

void drawWorldEdges()
{
  //top,right,bottom,left
  strokeWeight(1);
  stroke(255);
  line(-width + offsetX,-height + offsetY,(width * 2) + offsetX,-height +offsetY);
  line((width*2) + offsetX,-height + offsetY,(width*2)+offsetX,(height*2)+offsetY);
  line(-width+offsetX,(height*2)+offsetY,(width*2)+offsetX,(height*2)+offsetY);
  line(-width+offsetX,-height+offsetY,-width+offsetX,(height*2)+offsetY);
}


void updateMechanics()
{
  offsetX = width/2 -player.pos.x;
  offsetY = height/2 - player.pos.y;
  updateFacing();
  getMoveInput();
  updatePhysics();
}

void updatePhysics()
{
  if (player.pos.x < width*2 && player.pos.x >= -width)
  {
    player.pos.x += player.vel.x;
  }
  if (player.pos.y < height*2 && player.pos.y >= -height)
  {
    player.pos.y += player.vel.y;
  }
  player.vel.x += -player.vel.x * MOVEMENT_RESISTANCE;
  player.vel.y += -player.vel.y * MOVEMENT_RESISTANCE;
}

void getMoveInput()
{
  PVector vel = new PVector(mouseX - width/2,mouseY - height/2);
  float distMult = dist(mouseX,mouseY,width/2,height/2)/(Math.min(width,height) / 2);
  vel = vel.normalize().setMag(PLAYER_MAX_ACC * distMult);
  PVector newVel = new PVector(player.vel.x,player.vel.y);
  newVel.add(vel);
  if (newVel.mag() <= PLAYER_MAX_VEL)
  {
    player.vel.add(vel);
  }
}

void updateFacing()
{
  float x = mouseX;
  if (x == 0)
  {
    x = 0.1;
  }
  player.facing = PI/2 + atan((mouseY-(height/2))/(x-(width/2)));
  if (x < width/2)
  {
    player.facing += PI;
  }
}

void drawPlayer()
{
  pushMatrix();
  translate(width/2,height/2);
  rotate(player.facing);
  stroke(255);
  strokeWeight(2);
  fill(127);
  rect(-ROCKET_WIDTH/4,0-ROCKET_HEIGHT/2,ROCKET_WIDTH/2,ROCKET_HEIGHT*0.75);
  rect(-ROCKET_WIDTH/2,0,ROCKET_WIDTH/4,ROCKET_HEIGHT*0.5);
  rect(ROCKET_WIDTH/4,0,ROCKET_WIDTH/4,ROCKET_HEIGHT*0.5);
  popMatrix();
}

The Rocket class:

class Rocket
{
  PVector pos;
  PVector vel;
  float facing; //radians
  boolean pressingW,pressingA,pressingS,pressingD,mouseLClicked = false;
  public Rocket()
  {
    pos = new PVector(0,0);
    vel = new PVector(0,0);
    facing = 0;
  }

  public Rocket(int x,int y)
  {
    pos = new PVector(x,y);
    vel = new PVector(0,0);
    facing = 0;
  }
}

Solution

  • In the future, please try to post an MCVE instead of your whole project. Narrow it down to as few lines as code as possible. In your case, you only have to show two circles, and you can remove any other code such as showing the ship.

    That being said, your problem is caused by a combination of factors:

    • The location of your "stars" are stored in a PVector, which stores them as float values.
    • Your offsetX and offsetY are also float values.
    • However, pixels are always int values! (It doesn't really make sense to have a half a pixel.)

    What's happening is that your starts are moving at slightly different intervals, which your eyes detect as sporadic movement. This is caused by the float values reaching the next int value at different times.

    Let's say you have two positions: one at [0, 10] and another at [.6, 20]. Initially, these have the same pixel X value of 0, since pixels are always int values. Now let's say your xOffset is .5, and you add that to your positions to get [.5, 10] and [1.1, 20]. Now they have different pixel X values of 0 and 1, which makes it look like the second one moved but the first one did not! This is what's causing your problem.

    There are a number of ways to fix this, but the easiest approach might be to just initialize your star positions to int values:

    PVector vec = new PVector(int(random(-width,width*2)), int(random(-height,height*2)));
    

    This is also why your walls look okay: that's really only a single position that you're updating.