I want this code to effectively increase the smoothness of the transition between directions (it only works with one key at a time) so that I can use multiple keys. The problem is that whenever I change direction the "Player" stops and then continues in the new direction. I want the "Player" to smoothly transition between directions without having to fully release the active key before pressing the new one.
Main code:
Ball ball;
Player player1;
Player player2;
void setup() {
size(1368,768);
frameRate(60);
noStroke();
ball = new Ball(width/2, height/2, 30);
player1 = new Player(0, height/2, 30, 150);
player2 = new Player(width-30, height/2, 30, 150);
ball.speedX = -10;
ball.speedY = random(-5,5);
}
void draw() {
background(0);
ball.display();
ball.move();
player1.run();
player2.run();
//Collision
if (ball.top() < 0) {
ball.speedY = -ball.speedY;
}
if (ball.bottom() > height) {
ball.speedY = -ball.speedY;
}
if (ball.left() < 0) {
ball.speedX = 0;
ball.speedY = 0;
}
if (ball.right() > width) {
ball.speedX = 0;
ball.speedY = 0;
}
}
void keyPressed() {
player1.pressed((key == 'w' || key == 'W'), (key == 's' || key == 'S'));
player2.pressed((keyCode == UP), (keyCode == DOWN));
}
void keyReleased() {
player1.released((key == 'w' || key == 'W'), (key == 's' || key == 'S'));
player2.released((keyCode == UP), (keyCode == DOWN));
}
Player class code:
class Player {
float x, y;
int dy = 0;
float w, h;
float speedY = 5;
color c;
//Constructor
Player(float tempX, float tempY, float tempW, float tempH){
x = tempX;
y = tempY;
w = tempW;
h = tempH;
speedY = 0;
c = (255);
}
void run() {
display();
move();
}
void display() {
fill(c);
rect(x, y-h/2, w, h);
}
void move() {
y += dy * speedY;
}
void pressed(boolean up, boolean down) {
if (up) {dy = -1;}
if (down) {dy = 1;}
}
void released(boolean up, boolean down) {
if (up) {dy = 0;}
if (down) {dy = 0;}
}
}
Thanks in advance!
Add 2 attributes move_up and move_down to the class Player and set the attributes in
pressed respectively released:
class Player {
// [...]
boolean move_up = false, move_down = false;
void pressed(boolean up, boolean down) {
if (up) {move_up = true;}
if (down) {move_down = true;}
}
void released(boolean up, boolean down) {
if (up) {move_up = false;}
if (down) {move_down = false;}
}
}
Change speedY dependent on the attributes in move. Continuously reduce the speed if neither move_up not move_down is set (speedY = speedY * 0.95;). That causes that the player smoothly slows down if no key is pressed. If move_up or move_down is pressed the slightly change the speed dependent on the desired direction. Restrict the speed to a certain interval (speedY = max(-5.0, min(5.0, speedY));):
class Player {
// [...]
void move() {
if (!move_up && !move_down) {speedY *= 0.95;}
if (move_up) {speedY -= 0.1;}
if (move_down) {speedY += 0.1;}
speedY = max(-5.0, min(5.0, speedY));
y += speedY;
}
// [...]
}
Class Player:
class Player {
float x, y;
float w, h;
float speedY = 0.0;
color c;
boolean move_up = false, move_down = false;
//Constructor
Player(float tempX, float tempY, float tempW, float tempH){
x = tempX;
y = tempY;
w = tempW;
h = tempH;
c = (255);
}
void run() {
display();
move();
}
void display() {
fill(c);
rect(x, y-h/2, w, h);
println(y);
}
void move() {
if (!move_up && !move_down) {speedY *= 0.95;}
if (move_up) {speedY -= 0.1;}
if (move_down) {speedY += 0.1;}
speedY = max(-5.0, min(5.0, speedY));
y += speedY;
}
void pressed(boolean up, boolean down) {
if (up) {move_up = true;}
if (down) {move_down = true;}
}
void released(boolean up, boolean down) {
if (up) {move_up = false;}
if (down) {move_down = false;}
}
}
If you want smooth transitions, you're going to have to give up "adding a fixed integer distance" in the key handlers, and instead track which keys are down or not, and then accelerating/decelerating your player every time draw() runs. As simple illustration:
Box box;
boolean[] active = new boolean[256];
void setup() {
size(500,500);
box = new Box(width/2, height/2);
}
void draw() {
pushStyle();
background(0);
box.update(active); // First, make the box update its velocity,
box.draw(); // then, tell the box to draw itself.
popStyle();
}
void keyPressed() { active[keyCode] = true; }
void keyReleased() { active[keyCode] = false; }
With a simple box class:
class Box {
final float MAX_SPEED = 1, ACCELERATION = 0.1, DECELERATION = 0.5;
float x, y;
float dx=0, dy=0;
Box(float _x, float _y) { x=_x; y=_y; }
void draw() {
// We first update our position, based on current speed,
x += dx;
y += dy;
// and then we draw ourselves.
noStroke();
fill(255);
rect(x,y,30,30);
}
void update(boolean[] keys) {
if (keys[38]) { dy -= ACCELERATION ; }
else if (keys[40]) { dy += ACCELERATION ; }
else { dy *= DECELERATION; }
if (keys[37]) { dx -= ACCELERATION ; }
else if (keys[39]) { dx += ACCELERATION ; }
else { dx *= DECELERATION; }
dx = constrain(dx, -MAX_SPEED, MAX_SPEED);
dy = constrain(dy, -MAX_SPEED, MAX_SPEED);
}
}
The important part here is the update code, which updates the box's x and y velocity such that if a directional key is currently pressed, we increase the speeed (dx/dy) in that direction. Importantly, if no keys are pressed we also dampen the speed to that it returns to 0.
Finally, to make sure we don't end up with infinite speed, we cap the maximum allowed velocity.
Related
I am trying to get a circle to be able to jump and move left and right at the same time, but right now its either only jumping or moving forward at a time. Anyone know how to change my code to solve this? Thanks
float px,py,vx,vy,ax,ay;
boolean canJump = false;
void setup(){
size(600, 400);
ax = 0;
ay = .32;
vx = 0;
vy = 0;
px = 300;
py = 200;
}
int x = 50;
int y = 520;
void draw(){
background(0);
ellipse(px-15, py-30, 60, 60);
vx+=ax;
vy+=ay;
px+=vx;
py+=vy;
if( py > height ){
py = height;
vy = 0;
canJump = true;
}
player();
}
void player(){
fill(255);
rect(0, 550, 1000, 50);
}
void keyPressed(){
if(keyCode == RIGHT || key == 'd'){
px += 10;
}
if(keyCode == LEFT || key == 'a'){
px -= 10;
}
if(keyCode == UP){
if(canJump) {
vy = -10;
canJump = false;
}
}
}
So you can make your ball go both up and right or up and left by checking that both keys are pressed and then you can add to the velocity instead of the position to affect the trajectory of the ball. However, then you must slow down the x component of the velocity when it hits the ground, so I added a friction variable.
float px, py, vx, vy, ax, ay;
boolean canJump = false;
float bounce = 0.2;
float friction = 0.2;
void setup() {
size(600, 400);
ax = 0;
ay = .32;
vx = 0;
vy = 0;
px = 300;
py = 200;
}
int x = 50;
int y = 520;
void draw() {
background(0);
ellipse(px-15, py-30, 60, 60);
vx+=ax;
vy+=ay;
px+=vx;
py+=vy;
if ( py > height ) {
py = height;
vy = -bounce*vy;
vx = friction*vx;
canJump = true;
}
}
void keyPressed() {
if (keyCode == RIGHT && keyCode == UP) {
if (canJump) {
vy = -10;
vx += 5;
canJump = false;
}
} else if (keyCode == LEFT && keyCode == UP) {
if (canJump) {
vy = -10;
vx += -5;
canJump = false;
}
} else {
if (keyCode == RIGHT || key == 'd') {
vx += 5;
}
if (keyCode == LEFT || key == 'a') {
vx -= 5;
}
if (keyCode == UP) {
if (canJump) {
vy = -10;
canJump = false;
}
}
}
}
You can improve the implementation of your program by taking a look at processing's PVector class.
PVector pos;
PVector vel;
PVector acc;
float friction = 0.3;
float bounce = 0.5;
float diameter = 60;
boolean canJump = false;
void setup() {
size(600, 400);
pos = new PVector(300, 200);
vel = new PVector(0, 1);
acc = new PVector(0, 0.32);
}
void draw() {
background(0);
circle(pos.x, pos.y, diameter);
vel.add(acc);
pos.add(vel);
if (pos.y + diameter/2 > height) {
pos.set(pos.x, height-diameter/2);
vel.set(vel.x*friction, -vel.y*bounce);
canJump = true;
}
}
void keyPressed() {
boolean right = keyCode == RIGHT || key == 'd';
boolean left = keyCode == LEFT || key == 'a';
boolean up = keyCode == UP || key == 'w';
if (up && right && canJump) {
vel.add(5, -10);
canJump = false;
} else if (up && left && canJump) {
vel.add(-5, -10);
canJump = false;
} else {
if (up && canJump) {
vel.add(0, -10);
canJump = false;
}
if (right) {
vel.add(5, 0);
}
if (left) {
vel.add(-5, 0);
}
}
}
I want the ball.move(); function in the code below to continue running after pressing space once. It only works if I continue pressing the space key.
void draw() {
if (start == true) {ball.move();}
}
void keyPressed() {
if (key == ' '){start = true;}
}
void keyReleased() {
if (key == ' ') {start = false;}
}
It's for a Pong game I am making and each time the ball hits the edge it is teleported to the center of the canvas. That´s when I want to be able to start the ball movement manually again.
Here is the whole code:
Ball ball;
Player player1;
Player player2;
int scorePlayer1 = 0;
int scorePlayer2 = 0;
PFont font;
boolean start;
void setup() {
size(1368,768);
frameRate(144);
noStroke();
ball = new Ball(width/2, height/2, 30);
player1 = new Player(15, height/2, 30, 150);
player2 = new Player(width-15, height/2, 30, 150);
ball.speedX = 10;
}
void draw() {
background(0);
textSize(40);
textAlign(CENTER);
font = loadFont("Arial-Black-48.vlw");
textFont(font);
ball.display();
if (start == true) {ball.move();}
player1.run();
player2.run();
//Score
if (ball.left() < 0) {
scorePlayer2 = scorePlayer2 + 1;
ball.x = width/2;
ball.y = height/2;
}
if (ball.right() > width) {
scorePlayer1 = scorePlayer1 +1;
ball.x = width/2;
ball.y = height/2;
}
text(scorePlayer1, width/2-75, 50);
text(scorePlayer2, width/2+75, 50);
//Collision
if (ball.top() < 0) {
ball.speedY = -ball.speedY;
}
if (ball.bottom() > height) {
ball.speedY = -ball.speedY;
}
if (ball.left() < player1.right() && ball.y > player1.top()-10 && ball.y < player1.bottom()+10) {
ball.speedX = -ball.speedX;
ball.speedY = map(ball.y - player1.y, -player1.h/2, player1.h/2, -5, 5);
}
if (ball.right() > player2.left() && ball.y > player2.top()-10 && ball.y < player2.bottom()+10) {
ball.speedX = -ball.speedX;
ball.speedY = map(ball.y - player2.y, -player2.h/2, player2.h/2, -5, 5);
}
if (player1.bottom() > height) {
player1.y = height-player1.h/2;
}
if (player1.top() < 0) {
player1.y = player1.h/2;
}
if (player2.bottom() > height) {
player2.y = height-player1.h/2;
}
if (player2.top() < 0) {
player2.y = player1.h/2;
}
}
//Movement
void keyPressed() {
player1.pressed((key == 'w' || key == 'W'), (key == 's' || key == 'S'));
player2.pressed((keyCode == UP), (keyCode == DOWN));
if (key == ' '){start = true;}
}
void keyReleased() {
player1.released((key == 'w' || key == 'W'), (key == 's' || key == 'S'));
player2.released((keyCode == UP), (keyCode == DOWN));
if (key == ' ') {start = false;}
}
class Ball {
float x;
float y;
float d;
float speedX;
float speedY;
color c;
//Constructor
Ball(float tempX, float tempY, float tempD){
x = tempX;
y = tempY;
d = tempD;
speedX = 0;
speedY = 0;
c = (255);
}
void display() {
fill(c);
ellipse(x,y,d,d);
}
void move() {
x = x + speedX;
y = y + speedY;
}
//Collision help
float top() {
return y-d/2;
}
float bottom() {
return y+d/2;
}
float left() {
return x-d/2;
}
float right() {
return x+d/2;
}
}
class Player {
float x, y;
float w, h;
float speedY = 0.0;
color c;
boolean moveUp = false, moveDown = false;
//Constructor
Player(float tempX, float tempY, float tempW, float tempH){
x = tempX;
y = tempY;
w = tempW;
h = tempH;
speedY = 0;
c = (255);
}
void run() {
display();
move();
}
void display() {
fill(c);
rect(x-w/2, y-h/2, w, h);
}
//Movement
void move() {
if (!moveUp && !moveDown) {speedY = speedY * 0.85;}
if (moveUp) {speedY -= 1;}
if (moveDown) {speedY += 1;}
speedY = max(-7.0, min(7.0, speedY));
y += speedY;
}
void pressed(boolean up, boolean down) {
if (up) {moveUp = true;}
if (down) {moveDown = true;}
}
void released(boolean up, boolean down) {
if (up) {moveUp = false;}
if (down) {moveDown = false;}
}
//Collision help
float top() {
return y-h/2;
}
float bottom() {
return y+h/2;
}
float left() {
return x-w/2;
}
float right() {
return x+w/2;
}
}
In the code below, you are setting start to true when the key is pressed and false when the press is done. You can simply remove the line that sets start to false when the key press is done and the ball will always move when the space is pressed.
//Movement
void keyPressed() {
player1.pressed((key == 'w' || key == 'W'), (key == 's' || key == 'S'));
player2.pressed((keyCode == UP), (keyCode == DOWN));
if (key == ' '){start = true;}
}
void keyReleased() {
player1.released((key == 'w' || key == 'W'), (key == 's' || key == 'S'));
player2.released((keyCode == UP), (keyCode == DOWN));
//if (key == ' ') {start = false;}
}
I suppose you want the ball to stop moving when space is pressed again. This can be done in your keyPressed method as well by toggling start instead of setting it to true. Something like if (key == ' '){start = !start;}
Your problem is that when you release the space bar, it sets the start field to false. Then when the next draw() method is called, it sees that start==false and so doesn't move the ball.
If you remove that line from the keyReleased method, then it should run correctly?
I'm just learning how to use push and pop matrices. I want the car(object) to rotate on its axis and not(revolve) with the 0,0 of the translated coordinate. {Beginner programming hobbyist}
Tried re-translating the axes when I use rotate().
PImage c = new PImage();
Car forza = new Car();
Trees tr = new Trees();
float wrap = 100;
void setup(){
size(800,800);
}
void draw(){
background(0);
forza.update();
forza.display();
tr.road();
}
class Car {
float posX;
float posY;
float speed;
float accel;
float angle;
Car(){
posX = 0;
posY = 0;
speed = .9;
angle = sin(0);
accel = 0;
}
void update(){
pushMatrix();
translate(posX,posY);
if (keyPressed) {
if (key == 'd') {
angle += 1;
}else if (key == 'a'){
angle -= 1;
rotate(radians(angle));
} else if (key == 'w'){
posX += speed;
} else if (key == 's'){
posX -= speed;
}
}
popMatrix();
}
void display(){
pushMatrix();
translate(width/2,height/2);
rotate(radians(angle));
c = loadImage("car.jpg");
fill(255);
stroke(255);
imageMode(CENTER);
image(c,posX,posY,wrap,wrap);
line(0,0,posX,posY);
print(posX);
println(posY);
popMatrix();
}
}
class Trees {
float x;
float y;
Trees(){
//x = random(0,);
}
void trash(){
}
void road(){
fill(250,50);
rectMode(CENTER);
rect(width/2,height/2, width/2, height);
}
void show(){
}
}
I just wanna know the algorithm for it, and if any other way of doing the algorithm in terms of efficiency and aesthetics. ^^
If the object should rotate around its origin, then the rotation has to be done before the translation. Since operations like rotate() and translate() set a matrix and multiply the current matrix by the new matrix, this means that rotate() has to be done last before drawing the object. Even drawing an object at the a certain position (e.g. posX, posY) behaves like a translation.
You have to draw the car at position (0, 0). Then you've to rotate it. Finally translate it to its final position:
void display(){
pushMatrix();
translate(width/2,height/2);
pushMatrix();
translate(posX,posY);
rotate(radians(angle));
fill(255);
stroke(255);
imageMode(CENTER);
image(c,0,0,wrap,wrap);
popMatrix();
line(0,0,posX,posY);
popMatrix();
}
The matrix manipulation operations, change the matrix. The matrix is applied to the coordinates in of the drawing operations. The matrix operations in update are useless, because at the begin is a pushMatrix() at the end a popMatrix(), but nothing is draw at all.
It is a wast of performance, to load the image in every frame, do it once in the constructor of Car.
See the example:
Car forza;
Trees tr;
float wrap = 100;
void setup(){
size(800,800);
forza = new Car();
tr = new Trees();
}
void draw(){
background(0);
forza.update();
forza.display();
tr.road();
}
class Car {
float posX;
float posY;
float speed;
float accel;
float angle;
PImage c;
Car(){
posX = 0;
posY = 0;
speed = .9;
angle = sin(0);
accel = 0;
c = loadImage("car.jpg");
}
void update(){
if (keyPressed) {
if (key == 'd') {
angle += 1;
}else if (key == 'a'){
angle -= 1;
} else if (key == 'w'){
posX += speed;
} else if (key == 's'){
posX -= speed;
}
}
}
void display(){
pushMatrix();
translate(width/2,height/2);
pushMatrix();
translate(posX,posY);
rotate(radians(angle));
fill(255);
stroke(255);
imageMode(CENTER);
image(c,0,0,wrap,wrap);
popMatrix();
line(0,0,posX,posY);
popMatrix();
}
}
class Trees {
float x;
float y;
Trees(){
//x = random(0,);
}
void trash(){
}
void road(){
fill(250,50);
rectMode(CENTER);
rect(width/2,height/2, width/2, height);
}
void show(){
}
}
Our restart button is only appearing by itself. Our two tanks disappear when we run the game. We haven't coded the function of the button, we're just trying to figure out how to make the button appear in the lower left corner
We moved the code into the game object class, but it does not change anything
This is our game object class. The button is at the bottom.
import java.awt.Graphics;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class GameObject {
// Every moving object in our game is a separate GameObject. However, since there are different types of moving objects (eg. players and
// projectiles), each with their own variables and functions, we don't want to instantiate GameObjects (otherwise, we wouldn't be able
// to distinguish between a player and a projectile). Instead, we want to create derived (also known as child) classes to implement the
// separate functionality, and instantiate the derived/child classes instead. The purpose of this GameObject "parent" class is to
// provide the derived/child classes with a set of useful functions for handling physics interactions (eg. collisions with the terrain
// or the walls of the arena) between different GameObjects (eg. player/player, player/projectile, or even projectile/projectile). Note:
// depending on the features you implement, you may not end up using all of the functions provided in this class.
// Every GameObject contains its own velocity, position, size, and mass information (necessary for moving and handling physics
// interactions). We store this information in the GameObject class instead of the derived/child classes because every type of
// GameObject has the same set of velocity, position, size, and mass variables.
public double vX; // Velocity in the x direction. Positive if moving right. Negative if moving left.
public double vY; // Velocity in the y direction. Positive if moving down. Negative if moving up. Notice how it's flipped from the usual
// coordinate system in math!
public double posX; // Position along the x direction. Ranges from 0 (left edge) to maximumX (see below).
public double posY; // Position along the y direction. Ranges from 0 (top edge) to maximumY (see below).
public int width; // Width of the bounding box of the GameObject.
public int height; // Height of the bounding box of the GameObject.
public int mass; // Used in realistic physics collision calculations. Ignore it if you don't want to implement that feature.
public double radius; // Used for circular GameObjects only.
public int maximumX; // Maximum x position for a GameObject, equal to the arena width subtracted by the game object's width.
public int maximumY; // Maximum y position for a GameObject, equal to the arena height subtracted by the game object's height.
// Constructor. All derived (ie. child) classes call this constructor in their own constructors. The resulting derived/child class
// object can then call the other functions in this class.
public GameObject(int arenaWidth, int arenaHeight, double vX, double vY, double posX, double posY, int width, int height, int mass) {
this.vX = vX;
this.vY = vY;
this.posX = posX;
this.posY = posY;
this.width = width;
this.height = height;
this.mass = mass;
this.maximumX = arenaWidth - width;
this.maximumY = arenaHeight - height;
radius = Math.min(width, height) / 2.0;
}
// Note: No need to change this function since we're going to override it in the the child classes.
public boolean move(Map map, double translateX, double translateY) {
return false;
}
// Check if the calling GameObject currently intersects with the obj GameObject.
public boolean currentlyIntersects(GameObject obj) {
return (posX + width >= obj.posX && posY + height >= obj.posY && obj.posX + obj.width >= posX && obj.posY + obj.height >= posY);
}
// Check if the calling GameObject will intersect with the obj GameObject, after both have moved according to their velocities. A note
// of caution: what might go wrong if either player moves too fast?
public boolean willIntersect(GameObject obj) {
double nextX = posX + vX;
double nextY = posY + vY;
double nextObjX = obj.posX + obj.vX;
double nextObjY = obj.posY + obj.vY;
return (nextX + width >= nextObjX && nextY + height >= nextObjY && nextObjX + obj.width >= nextX && nextObjY + obj.height >= nextY);
}
// Clip the calling GameObject to within the arena's x bounds, if it has moved outside the arena along the x direction.
public boolean xClip() {
if (posX < 0) {
posX = 0;
return true;
} else if (posX > maximumX) {
posX = maximumX;
return true;
}
return false;
}
// Clip the calling GameObject to within the arena's y bounds, if it has moved outside the arena along the y direction.
public boolean yClip() {
if (posY < 0) {
posY = 0;
return true;
} else if (posY > maximumY) {
posY = maximumY;
return true;
}
return false;
}
// If the calling GameObject will move outside the arena along either direction (after moving according to its velocity), this function
// tells you which of the four edges of the arena it hit. If the calling GameObject will stay within the bounds of the arena, this
// function returns null.
public Direction hitEdgeDirection() {
if (posX + vX < 0) {
return Direction.LEFT;
} else if (posX + vX > maximumX) {
return Direction.RIGHT;
}
if (posY + vY < 0) {
return Direction.UP;
} else if (posY + vY > maximumY) {
return Direction.DOWN;
} else {
return null;
}
}
// If the calling GameObject will intersect with the "other" GameObject (after both move according to their velocities), this function
// tells you which of the four sides of the calling GameObject that the "other" GameObject hit. If the calling GameObject will not
// intersect with the "other" GameObject, this function returns null. Note: this function is great for figuring out when and where two
// rectangles intersect, but is it a good choice for handling circle/rectangle or circle/circle intersections?
public Direction hitObjectDirection(GameObject other) {
if (this.willIntersect(other)) {
double dx = other.posX + other.width / 2.0 + other.vX - (posX + width / 2.0 + vX);
double dy = other.posY + other.height / 2.0 + other.vY - (posY + height / 2.0 + vY);
double theta = Math.acos(dx / (Math.sqrt(dx * dx + dy * dy)));
double diagTheta = Math.atan2(height / 2.0, width / 2.0);
if (theta <= diagTheta) {
return Direction.RIGHT;
} else if (theta <= Math.PI - diagTheta) {
if (dy > 0) {
return Direction.DOWN;
} else {
return Direction.UP;
}
} else {
return Direction.LEFT;
}
} else {
return null;
}
}
// Change the calling GameObject's velocity (to simulate a "bouncing" effect) based on which direction it intersected another GameObject
// or the edge of the arena. If the passed in direction is null, this function does nothing (why is this a good idea?). This function is
// best used with the hitEdgeDirection and hitObjectDirection functions above.
public void bounce(Direction d) {
if (d == null) {
return;
}
// Note: We probably should use a "switch" statement here instead. But for pedagogical purposes it's left as a simple if/else
// conditional.
if (d == Direction.UP) {
vY = Math.abs(vY);
} else if (d == Direction.DOWN) {
vY = -Math.abs(vY);
} else if (d == Direction.LEFT) {
vX = Math.abs(vX);
} else if (d == Direction.RIGHT) {
vX = -Math.abs(vX);
}
}
// TODO: (Challenge!) If you want to implement realistic sphere-sphere collisions that take into account the laws of physics, do so in
// the function below.
public boolean bounceWith(GameObject otherObj, Map map, long frames, double[] actualVelocities) {
return false;
}
// Calculate the distance from (pointX, pointY)---perhaps representing the center of a circle---to the closest point on a rectangle
// bounded by minX (left), maxX (right), minY (top), and maxY (bottom). If the point is inside the rectangle, this function returns 0.
public double pointToRectSqrDist(double minX, double maxX, double minY, double maxY, double pointX, double pointY) {
double dx = Math.max(Math.max(minX - pointX, 0), pointX - maxX);
double dy = Math.max(Math.max(minY - pointY, 0), pointY - maxY);
return dx * dx + dy * dy;
}
// Rotate the point (x, y) "degrees" degrees around (centerX, centerY) in counterclockwise fashion, and return the resulting point in an
// array of length 2. If the returned array is "result", then (result[0], result[1]) is the final point.
public double[] rotatePoint(double centerX, double centerY, double degrees, double x, double y) {
double s = Math.sin(Math.toRadians(degrees));
double c = Math.cos(Math.toRadians(degrees));
x -= centerX;
y -= centerY;
double xNew = x * c - y * s;
double yNew = x * s + y * c;
double[] result = new double[2];
result[0] = xNew + centerX;
result[1] = yNew + centerY;
return result;
}
// Note: No need to change this function since we're going to override it in the the child classes.
public void draw(Graphics g) {
}
public static void main(String []args){
JButton b= new JButton("Reset");
JFrame f = new JFrame();
f.setSize(1200,800);
f.setVisible(true);
f.getDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JPanel p = new JPanel();
f.add(p);
p.add(b);
b.setSize(50,50);
b.setVisible(true);
b.setLocation(50, 50);
}
}
This is our arena class:
// TODO: Feel free to import any other libraries that you need.
import java.awt.*;
import java.awt.event.*;
import java.util.ArrayList;
import javax.swing.*;
#SuppressWarnings("serial")
public class Arena extends JPanel {
public int arenaWidth;
public int arenaHeight;
public Player player1;
public Player player2;
Timer timer;
public static int INTERVAL = 35;
public long lastTick;
// TODO: Add other variables to keep track of the game state or other game objects (eg. the map) that will be in your game. Don't forget
// to instantiate them in reset()!
// Constructor. Called inside Game.java for setting up the Arena on game start.
public Arena() {
// Create a timer that calls the tick() function every INTERVAL milliseconds. Every call of the tick() function is a "frame".
timer = new Timer(INTERVAL, new ActionListener() {
public void actionPerformed(ActionEvent e) {
tick();
}
});
lastTick = System.currentTimeMillis();
timer.start();
setFocusable(true);
// TODO: To recognize key presses, you need to fill in the following.
addKeyListener(new KeyAdapter() {
public void keyPressed(KeyEvent butthole) {
if (butthole.getKeyCode() == KeyEvent.VK_W) {
player1.isWPressed = true;
}
if (butthole.getKeyCode() == KeyEvent.VK_S) {
player1.isSPressed = true;
}
if (butthole.getKeyCode() == KeyEvent.VK_A) {
player1.isAPressed = true;
}
if (butthole.getKeyCode() == KeyEvent.VK_D) {
player1.isDPressed = true;
}
if (butthole.getKeyCode() == KeyEvent.VK_SPACE) {
player1.isSpacePressed = true;
}
if (butthole.getKeyCode() == KeyEvent.VK_UP) {
player2.isUpPressed = true;
}
if (butthole.getKeyCode() == KeyEvent.VK_DOWN) {
player2.isDownPressed = true;
}
if (butthole.getKeyCode() == KeyEvent.VK_LEFT) {
player2.isLeftPressed = true;
}
if (butthole.getKeyCode() == KeyEvent.VK_RIGHT) {
player2.isRightPressed = true;
}
if (butthole.getKeyCode() == KeyEvent.VK_ENTER) {
player2.isEnterPressed = true;
}
}
public void keyReleased(KeyEvent butthole) {
if (butthole.getKeyCode() == KeyEvent.VK_W) {
player1.isWPressed = false;
}
if (butthole.getKeyCode() == KeyEvent.VK_S) {
player1.isSPressed = false;
}
if (butthole.getKeyCode() == KeyEvent.VK_A) {
player1.isAPressed = false;
}
if (butthole.getKeyCode() == KeyEvent.VK_D) {
player1.isDPressed = false;
}
if (butthole.getKeyCode() == KeyEvent.VK_SPACE) {
player1.isSpacePressed = false;
}
if (butthole.getKeyCode() == KeyEvent.VK_UP) {
player2.isUpPressed = false;
}
if (butthole.getKeyCode() == KeyEvent.VK_DOWN) {
player2.isDownPressed = false;
}
if (butthole.getKeyCode() == KeyEvent.VK_LEFT) {
player2.isLeftPressed = false;
}
if (butthole.getKeyCode() == KeyEvent.VK_RIGHT) {
player2.isRightPressed = false;
}
if (butthole.getKeyCode() == KeyEvent.VK_ENTER) {
player2.isEnterPressed = false;
}
}
});
}
// Resets the game to its initial state.
public void reset() {
this.removeAll();
this.setBackground(Color.WHITE);
this.setOpaque(true);
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
arenaWidth = (int) screenSize.getWidth();
arenaHeight = (int) screenSize.getHeight();
player1 = new Player(arenaWidth, arenaHeight, 100, 100, 1);
player2 = new Player(arenaWidth, arenaHeight, arenaWidth - Player.INIT_SIZE - 100, arenaHeight - Player.INIT_SIZE - 100, 2);
requestFocusInWindow();
}
// Function called once per "frame".
void tick() {
// While tick() should be called once every INTERVAL amount of time, there's no guarantee of that, particularly if you have a lot
// of background apps running. Thus, we need to calculate the time difference (timeDelta) between every two calls of the tick()
// function. Note: 1 divided by this difference is commonly known as the "frames per second", or fps.
long currentTime = System.currentTimeMillis();
long timeDelta = (currentTime - lastTick)/35;
lastTick = currentTime;
if ((player1.isWPressed && player1.isSPressed) ||
(!player1.isWPressed && !player1.isSPressed)) {
player1.vX = 0;
player1.vY = 0;
} else if (player1.isWPressed) {
//System.out.println("Up");
player1.vX = Math.cos(player1.RotationDegree*Math.PI/180)*player1.speed * timeDelta;
player1.vY = Math.sin(player1.RotationDegree*Math.PI/180)*player1.speed * timeDelta;
//player1.posY -= player1.speed*timeDelta;
// MOVE FORWARD
} else if (player1.isSPressed) {
//System.out.println("Down");
player1.vX = -Math.cos(player1.RotationDegree*Math.PI/180)*player1.speed * timeDelta;
player1.vY = -Math.sin(player1.RotationDegree*Math.PI/180)*player1.speed * timeDelta;
//player1.posY += player1.speed*timeDelta;
// MOVE BACKWARD;
}
if (player1.isAPressed) {
//System.out.println("Left");
player1.RotationDegree -= player1.rotateSpeed* timeDelta;
// MOVE BACKWARD;
}
if (player1.isDPressed) {
//System.out.parintln("Right");
player1.RotationDegree += player1.rotateSpeed* timeDelta;
// MOVE BACKWARD;
}
if(player1.RotationDegree > 360) {
player1.RotationDegree -= 360;
}
else if(player1.RotationDegree < 0) {
player1.RotationDegree += 360;
}
player1.move(null, player1.vX, player1.vY);
if ((player2.isUpPressed && player2.isDownPressed) ||
(!player2.isUpPressed && !player2.isDownPressed)) {
player2.vX = 0;
player2.vY = 0; }
else if (player2.isUpPressed) {
//System.out.println("Up");
player2.vX = Math.cos(player2.RotationDegree*Math.PI/180)*player2.speed* timeDelta;
player2.vY = Math.sin(player2.RotationDegree*Math.PI/180)*player2.speed* timeDelta;
//player2.posY -= player2.speed*timeDelta;
// MOVE FORWARD
}
else if (player2.isDownPressed) {
//System.out.println("Down");
player2.vX = -Math.cos(player2.RotationDegree*Math.PI/180)*player2.speed * timeDelta;
player2.vY = -Math.sin(player2.RotationDegree*Math.PI/180)*player2.speed * timeDelta;
//player2.posY += player2.speed*timeDelta;
// MOVE BACKWARD;
}
if (player2.isLeftPressed) {
//System.out.println("Left");
player2.RotationDegree -= player2.rotateSpeed*timeDelta;
// MOVE BACKWARD;
}
if (player2.isRightPressed) {
//System.out.println("Right");
player2.RotationDegree += player2.rotateSpeed*timeDelta;
// MOVE BACKWARD;
}
if(player2.RotationDegree > 360) {
player2.RotationDegree -= 360;
}
else if(player2.RotationDegree < 0) {
player2.RotationDegree += 360;
}
player2.move(null, player2.vX, player2.vY);
player1.currentReload -= timeDelta;
if (player1.currentReload <= 0)
;
{
if (player1.isSpacePressed) {
// create bullet and fire
BasicWeapon newBullet = new BasicWeapon(arenaWidth, arenaHeight, player1.posX + player1.radius, player1.posY + player1.radius, player1);
player1.bullets.add(newBullet);
player1.currentReload = player1.MaxReload;
}
}
ArrayList<PlayerProjectile> bulletsToDelete1 = new ArrayList<PlayerProjectile>();
for (int i = 0; i < player1.bullets.size(); i++) {
PlayerProjectile bulletToChange = player1.bullets.get(i);
bulletsToDelete1.add(bulletToChange);
}
player1.bullets.removeAll(bulletsToDelete1);
// TODO: Update the game state each frame. This can be broken into the following steps:
// Step 1: Handle the keys pressed during the last frame by both players and calculate their resulting velocities/orientations.
// Step 2: Move the players and detect/handle player/player collisions and player/terrain collisions.
// Step 3: Decide whether a bullet should be fired for each player and create new bullet(s) if so. Also, handle reload mechanics.
// Step 4: Move all bullets via their calculated velocities (up to bullet range). Handle bullet/player & bullet/terrain collisions.
// Step 5: Decide whether the game has ended. If so, stop the timer and print a message to the screen indicating who's the winner.
// Note: If you implement other features (eg. weapon swapping, damage counters...etc.), you might also need to add more steps above.
// Update the display: this function calls paintComponent as part of its execution.
repaint(); }
// TODO: Draw all of the objects in your game.
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
//AffineTransform at = AffineTransoform.getTranslateInstance();
player1.draw(g);
player2.draw(g);
}
// Returns the dimensions of the Arena (for properly resizing the JPanel on the screen).
#Override
public Dimension getPreferredSize() {
return new Dimension(arenaWidth, arenaHeight);
}
}
THIS IS OUR PLAYER CLASS
// TODO: Feel free to import any other libraries that you need.
//import java.*;
import java.awt.*;
import java.util.ArrayList;
//import javax.swing.*;
//import java.awt.event.*;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class Player extends GameObject {
public static void main(String []args){
}
// TODO: Set these values as you see fit. However, which variables should you not set as negative numbers? Which variables should you
// not set as zero? Which v'ariables should you not set as a very large positive number? Why?
public static final int INIT_SIZE = 30;
public static final int INIT_MASS = 0;
public static final int INIT_DAMAGE = 0;
public static final double INIT_SPEED = 10;
public static final double INIT_ROTATE_SPEED = 10;
public static final int INIT_HEALTH = 100;
// Member variables of the player that you can change over the course of the game.
public int damage = INIT_DAMAGE;
public double speed = INIT_SPEED;
public double rotateSpeed = INIT_ROTATE_SPEED;
public int health = INIT_HEALTH;
public double orientation = 0;
public int id;
// TODO: You may need to set up extra variables to store the projectiles fired by this player, the reload status/time of this player,
// the key press/release status of this player, and any other player-related features you decide to implement. Make sure to update the
// constructor appropriately as well!
long currentReload = 0;
long MaxReload = BasicWeapon.INIT_RELOAD;
ArrayList<PlayerProjectile> bullets = new ArrayList<PlayerProjectile>();
double RotationDegree = 0;
boolean isWPressed = false;
boolean isSPressed = false;
boolean isAPressed = false;
boolean isDPressed = false;
boolean isSpacePressed = false;
boolean isEnterPressed = false;
boolean isUpPressed = false;
boolean isDownPressed = false;
boolean isLeftPressed = false;
boolean isRightPressed = false;
boolean isLeftCLickPressed = false;
// Constructor that calls the super (ie. parent) class's constructor and instantiates any other player-specific variables.
public Player(int arenaWidth, int arenaHeight, double startPosX, double startPosY, int id) {
super(arenaWidth, arenaHeight, 0, 0, startPosX, startPosY, INIT_SIZE, INIT_SIZE, INIT_MASS);
this.id = id;
}
// TODO: This function should move the player and handle any player-terrain interactions.
#Override
public boolean move(Map map, double translateX, double translateY) {
posX += translateX;
posY += translateY;
xClip();
yClip();
return false;
}
//UPDATE PLAYER POSTION HERE
//}
public void draw(Graphics g) {
// TODO: Draw the barrel(s) for the player here
double xChords[] = new double[4];
double YChords[] = new double[4];
xChords[0] = posX + 0.6 * width;
xChords[1] = posX + 0.6 * width;
xChords[2] = posX + 1.5 * width;
xChords[3] = posX + 1.5 * width;
YChords[0] = posY + 0.4 * height;
YChords[1] = posY + 0.5 * height;
YChords[2] = posY + 0.5 * height;
YChords[3] = posY + 0.4 * height;
double[] point0 = rotatePoint(posX + 0.5 * width, posY + 0.5 * height, RotationDegree, xChords[0], YChords[0]);
double[] point1 = rotatePoint(posX + 0.5 * width, posY + 0.5 * height, RotationDegree, xChords[1], YChords[1]);
double[] point2 = rotatePoint(posX + 0.5 * width, posY + 0.5 * height, RotationDegree, xChords[2], YChords[2]);
double[] point3 = rotatePoint(posX + 0.5 * width, posY + 0.5 * height, RotationDegree, xChords[3], YChords[3]);
int rotatedPointsX[] = new int[4];
int rotatedPointsY[] = new int[4];
rotatedPointsX[0] = (int)Math.round(point0[0]);
rotatedPointsX[1] = (int)Math.round(point1[0]);
rotatedPointsX[2] = (int)Math.round(point2[0]);
rotatedPointsX[3] = (int)Math.round(point3[0]);
rotatedPointsY[0] = (int)Math.round(point0[1]);
rotatedPointsY[1] = (int)Math.round(point1[1]);
rotatedPointsY[2] = (int)Math.round(point2[1]);
rotatedPointsY[3] = (int)Math.round(point3[1]);
g.drawPolygon(rotatedPointsX, rotatedPointsY, 4);
g.setColor(Color.BLACK);
g.fillPolygon(rotatedPointsX, rotatedPointsY, 4);
if (id == 1) {
g.setColor(new Color(255, 215, 0));
} else if (id == 2) {
g.setColor(Color.RED);
}
// Body
g.fillOval((int) posX, (int) posY, width, height);
g.setColor(Color.BLACK);
g.drawOval((int) posX, (int) posY, width, height);
// TODO: Draw the health bar for the player here.
}
}
Only the button appears, and player1 and player2 don't.
I think what you are trying to achieve is something like :
public static void main(String []args){
JButton button= new JButton("Reset");
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JPanel panel = new JPanel();
panel.add(button);
frame.add(panel, BorderLayout.NORTH);
frame.add(new Arena(), BorderLayout.CENTER);
frame.pack();
frame.setVisible(true);
}
For future question please do not post so much code. See: don't just copy in your entire program!
I am developing a two player game in Processing (running on Java). One user will control his character using the WASD keys and the other will control movement using the arrow keys. The problem I am having is that using keyPressed negates WASD when arrows are pressed and vice versa. I have been messing around with it for a really long time. Does anyone know a work around or notice something that I am doing wrong?
//global variables
int wide = 600; //canvas width
int tall = 600; //canvas height
int s = 50; //player size
float speed = 2.5; //player movement speed
//colors
int redColor = #CB4646; //player 1 color
int blueColor = #4652CB; //player 2 color
int backgroundColor = #DBE3B3; //background color
float player1X = 600/3-s; //HOW COME width/3 DOESN'T WORK??????????
float player2X = 600*2/3;
float playerY = 600/2-(s/2);
//players
Player player1 = new Player(player1X, playerY, s, speed, "wasd", redColor); //player 1
Player player2 = new Player(player2X, playerY, s, speed, "arrows", blueColor); //player 2
//setup
void setup(){
background(backgroundColor);
size(wide, tall);
smooth();
println(player2.controls);
}
//draw
void draw(){
background(backgroundColor);
player1.usePlayer();
player2.usePlayer();
}
class Player{
//class variables
float x; // x position
float y; // y position
int s; //size
float speed; //speed
String controls; //controls
int colors; //player color
char keyControls [] = new char [4];
//construct
Player(float tempX, float tempY, int tempS , float tempSpeed, String tempControls, int tempColors){
x = tempX;
y = tempY;
s = tempS;
speed = tempSpeed;
controls = tempControls;
colors = tempColors;
}
void usePlayer(){
// draw player
fill(colors);
rect(x, y, s, s);
//move player
keyPressed();
//wraparound
boundaries();
}
void keyPressed(){
//sets controls for wasd
if(controls == "wasd"){
if(key == 'w' || key == 'W'){
y -= speed; //move forwards
}
if(key == 's' || key == 'S'){
y += speed; //move backwards
}
if(key == 'd' || key == 'D'){
x += speed; //move right
}
if(key == 'a' || key == 'A'){
x -= speed; //move left
}
}
//sets controls for arrows
if(controls == "arrows"){
if(key == CODED){
if(keyCode == UP){
y -= speed; //move forwards
}
if(keyCode == DOWN){
y += speed; //move backwards
}
if(keyCode == RIGHT){
x += speed; //move right
}
if(keyCode == LEFT){
x -= speed; //move left
}
}
}
}
//pacman style wraparound
void boundaries(){
if(x == width) x = 2;
if(y == height) y = 2;
if(x == 0) x = width-s;
if(y == 0) y = height-s;
}
}
Track your keys independently, don't rely on the event globals.
boolean[] keys = new int[255];
void keyPressed() {
keys[keyCode] = true;
}
void keyReleased() {
keys[keyCode] = false;
}
void draw() {
updatePlayers();
drawStuff();
}
void updatePlayers() {
if(keys[LEFT]) { p1.move(-1,0); }
if(keys[RIGHT]) { p1.move(1,0); }
if(keys[UP]) { p1.move(0,-1); }
if(keys[DOWN]) { p1.move(0,1); }
if(keys['a']) { p2.move(-1,0); }
if(keys['d']) { p2.move(1,0); }
if(keys['w']) { p2.move(0,-1); }
if(keys['s']) { p2.move(0,1); }
}
Note this has to be a series of if statements, because you want to handle all pressed keys. If someone's holding left and right, p1 will move left, and right.
Also note that this example code doesn't filter for the higher-than-255 codes you get for special keys, so you probably want to put an "if(keyCode>255) return" at the start of the event handlers.
Is key a global variable? I don't see it getting passed to the Player. If it's global, then it can only hold one key at a time, which precludes controlling two players at once.
Here is my Arduino/Processing code that I used to handle simultaneous key presses (to move diagonally). It fixes the issue with boolean[] cannot be cast to int[] error as shown by #Brannon and uses keyCodes instead of key.
import processing.serial.*;
boolean[] keys = new boolean[255];
Serial port;
void setup() {
port = new Serial(this, Serial.list()[1], 9600);
}
void draw() {
// loop through boolean array and see which ones (index = keyCode)
// are true, then write to them.
for(int i = 0; i < 255; i++) {
if(keys[i]) {
if (i == 87) { port.write('w'); }
if (i == 65) { port.write('a'); }
if (i == 83) { port.write('s'); }
if (i == 68) { port.write('d'); }
}
}
}
void keyPressed() {
keys[keyCode] = true;
}
void keyReleased() {
keys[keyCode] = false;
}