using hue to increment a checkpoint counter java - java

I am creating a racing game using java and have got a checkpoint counter and a lap counter. I have drawn lines on the track to increment the checkpoint counter. When the checkpoint counter reaches 9 and they pass over the blue line the lap counter then increments. The problem is that when the car goes over a line the checkpoint counter or the lap counter will either keep incrementing or it will not move at all.
Player player;
float scaleFactor, translateX, translateY;
color c;
int counterlap=0;
int countercheck=0;
void setup() {
size(displayWidth, displayHeight);
frame.setResizable(true); //make the screen adjustable
background=loadImage("track.png");
player = new Player(100, 100); //initialize the player object
scaleFactor = 2;
}
PImage background; //define an image for the background
void draw() {
background(0);
imageMode(CORNER);
pushMatrix();
translate(width/2, height/2);
scale(scaleFactor);
translate(-width/2, -height/2);
pushMatrix();
translate(-player.position.x+width/2, -player.position.y+height/2);
image(background, 0, 0, background.width, background.height); //draw background
popMatrix();
popMatrix();
c = get(width/2, height/2);
println(hue(c));
player.draw(); //draws the player
text("Laps "+counterlap, width-50, 20);//lapcounter
if (countercheck>=9) {
if ((hue(c)>135)&&(hue(c)<141)) {
counterlap++;
if ((counterlap>=1)&&(countercheck>=9)) {
countercheck=0;
}
}
}
text("Checkpoint "+countercheck+"/9", width-200, 20);//checkpoint counter
if ((hue(c)>249)&&(hue(c)<254)) {
countercheck++;
}
}
class Player { //creates the player class for the player object
PVector position, velocity; //defines position & velocity vectors
float vel, accel, heading; //scalar magnitudes
PImage car;
Player(float xposition, float yposition) {
position = new PVector(xposition, yposition); //initialize everything and pass the spawn point
velocity = new PVector(0, 0);
accel=0;
car = loadImage("car.png"); //loads the image for the player
}
void draw() {
pushMatrix();
translate(width/2, height/2); //translates car origin to the position vector
scale(scaleFactor);
rotate(heading+PI); //rotates to the current heading
imageMode(CENTER); //centers the car image for better rotation
tint(color(255, 0, 255)); //colour of the car
image(car, 0, 0, 64, 40); //draws the image and resizes to 64x40
noTint(); //disable tint for remainder of frame
popMatrix();
if (up) { //acclerate when up is pressed
accel=0.15;
} else { //otherwise no accleration
accel=0;
}
if (down) { //brake/reverse
accel=-0.15;
}
if (left) {
heading-=0.04*sqrt(abs(vel)); //
}
if (right) {
heading+=0.04*sqrt(abs(vel));
}
if (!(up||down||left||right)) {
}
vel+=accel;
velocity=PVector.fromAngle(heading);
velocity.setMag(vel);
if (hue(c)==97.48252) {
vel*=0.9;
} else {
vel*=0.99;
}
velocity.limit(50);
position.add(velocity);
if (position.x>background.width) {
position.x=background.width;
velocity.x*=-0.1;
}
if (position.x<0) {
position.x=0;
velocity.x*=-0.1;
}
if (position.y>background.height) {
position.y=background.height;
velocity.y*=-0.1;
}
if (position.y<0) {
position.y=0;
velocity.y*=-0.1;
}
}
}
boolean up, down, left, right;
void mouseWheel(MouseEvent e)
{
scaleFactor *= pow(2, e.getAmount() /10F);
translateX -= e.getAmount() * mouseX / 100;
translateY -= e.getAmount() * mouseY / 100;
}
void keyPressed() {
if (keyCode == UP) {
up = true;
text("D", width-500, 20);
}
if (keyCode == DOWN) {
down = true;
text("R", width-500, 20);
}
if (keyCode == LEFT) {
left = true;
}
if (keyCode == RIGHT) {
right = true;
}
if (key == 'r')
{
scaleFactor = 2;
translateX = 0;
translateY = 0;
}
}
void keyReleased() {
if (keyCode == UP) {
up = false;
}
if (keyCode == DOWN) {
down = false;
}
if (keyCode == LEFT) {
left = false;
}
if (keyCode == RIGHT) {
right = false;
}
}

There are two problems in the current approach:
If your player stays on the line, for each frame he stays there, you will count a lap.
If your player passes quickly from a point on one side the lap line to another point on the other side... he has crossed the line, but if there was no intermediate frame when he stood right on top, you will not count a lap.
To fix this, instead of looking at "where is the player in this frame", you should check "has the player moved, in this last frame, from being on one side of the line to being on top or on the other side of the line; -- and has he done the same for all N checkpoint lines before doing so". Since this can happen only once, and exactly once for each lap, you will now count the number of laps correctly.
This means that you cannot rely on hue alone; unless you make sure that, no matter the speed of the car, it is impossible to trigger "from one side to the other without ever being right on top" in a single frame.

Related

Why it is showing only one box in export of processing.org java pdf?

Here is my code down below, when in run it shows multiple boxes as I wanted but when I try to export, it only shows a single box at that frame position.I tried generating output at particular frame but its same issue
import processing.pdf.*;
int ofs = 500;
boolean record;
void setup() {
size(1900, 1080, P3D);
}
void draw() {
if (record) {
beginRaw(PDF, "box.pdf");
}
background(255);
translate(width/2, height/2, -800);
rotateZ(0.3);
rotateY(frameCount/500.0);
float x = sin(radians(frameCount*0.1));
for ( int xo = -ofs; xo<=ofs; xo += 100) {
for ( int yo = -ofs; yo<=ofs; yo += 100) {
pushMatrix();
translate ( xo, yo, 0);
box(200*x);
fill(155,150,90);
strokeWeight(3*x);
popMatrix();
if (record) {
endRaw();
record = false;
}
}
}
}
void keyPressed() {
if (key == 'r') {
record = true;
}
}
So close! :)
Perhaps the formatting might have made it harder to spot the issue.
You're calling endRaw(); (which wraps up pdf recording) nested in the for loops.
This means it's only picking up the first box since record is set to false so the other boxes are ignored.
I recommend using Edit > Auto Format (CMD + T / Ctrl + T).
Simply move the if(record) after the nested for loops:
import processing.pdf.*;
int ofs = 500;
boolean record;
void setup() {
size(1400, 580, P3D);
}
void draw() {
if (record) {
beginRaw(PDF, "box.pdf");
}
background(255);
translate(width/2, height/2, -800);
rotateZ(0.3);
rotateY(frameCount/500.0);
float x = sin(radians(frameCount*0.1));
for ( int xo = -ofs; xo<=ofs; xo += 100) {
for ( int yo = -ofs; yo<=ofs; yo += 100) {
pushMatrix();
translate ( xo, yo, 0);
box(200*x);
fill(155, 150, 90);
strokeWeight(3*x);
popMatrix();
}
}
// finish recording to PDF only after everything was drawn
if (record) {
endRaw();
record = false;
}
}
void keyPressed() {
if (key == 'r') {
record = true;
}
}

How to make multiple objects move randomly?

I'm trying to make a sort of Agar.io clone in Processing. I have spawned in a lot of food dots, but I wanted to also make them move about and bounce off the edges of the screen border to add some flair. However I am not too sure how to go about making the dots all move about randomly.
ArrayList<Ellipse> ellipse = new ArrayList <Ellipse>();
//Images
PImage background;
int x = 2;
//Words
PFont arial;
void setup(){
size(1920,1080);
//Background change
if (x == 1){
background = loadImage("backdrop1.jpg");
}
if (x == 2){
background = loadImage("backdrop2.jpg");
}
//Creating the font
arial = createFont ("Arial", 16, true); //the true is for antialiasing
//Load from text file
//tbd...
//Adding the food ellipses
for(int foodSpawn = 0; foodSpawn < 50; foodSpawn++){
ellipse.add(new Ellipse(random(100,1820),random(100,980), 50, 50));
}
}
void draw(){
background(background);
for(int i = 0; i<ellipse.size(); i++){
Ellipse e = ellipse.get(i);
fill(#62C3E8);
ellipse(e.xLoc,e.yLoc, e.eWidth, e.eHeight);
}
}
class Ellipse {
float xLoc;
float yLoc;
float eWidth;
float eHeight;
public Ellipse(float xLoc, float yLoc, float eWidth, float eHeight){
this.xLoc = xLoc;
this.yLoc = yLoc;
this.eWidth = eWidth;
this.eHeight = eHeight;
}
}
The ellipses already have position attributes, so simply adding a method to move them should work. If you want them to collide realistically with the walls, you'll need to give each ellipse an initial random velocity. Then, you update the position at set time intervals based on the current velocity and the interval length. E.g.:
public void move() {
// Note: these are signed, and xvel and vyel are in pixels/second
float x_move_dist = this.xvel*time_int
float y_move_dist = this.yvel*time_int
// Update xloc
// Check collision with left wall
if (this.xloc + x_move_dist - this.eWidth/2 < 0) {
// Assuming conservation of momentum, we can reflect the movement off the wall
this.xloc = -(this.xloc + x_move_dist + this.eWidth/2)
}
// Check collision with right wall
else if (this.xloc + x_move_dist + this.eWidth/2 > 1920) {
// Again, reflect off wall
this.xloc = 1920 - ((this.xloc + x_move_dist) - 1920) - this.eWidth/2
}
// Otherwise, just update normally
else {
this.xloc = this.xloc + x_move_dist
}
// Update yloc
// Check collision with bottom wall
if (this.yloc + y_move_dist - this.eHeight/2 < 0) {
// Again, reflect off wall
this.yloc = -(this.yloc + y_move_dist) + this.eHeight/2
}
// Check collision with top wall
else if (this.yloc + y_move_dist + this.eHeight/2 > 1080) {
// Again, reflect off wall
this.yloc = 1920 - ((this.yloc + y_move_dist) - 1920) - this.eHeight/2
}
// Otherwise, just update normally
else {
this.yloc = this.yloc + y_move_dist
}
}

How to fix Java listening to more than 1 key at a time?

Me and my partner are attempting to create the game Pong for our computer science final project. We created a reference code where 2 cubes can be controlled upwards and downwards and it works fine. The problem occurs when attempting to control both cubes at the same time (only 1 cube will move at a time). We want to make both cubes move at the same time.
WE want to say that:
yPos - is the y position of the black cube
xPos - is the x position of the black cube
xPos2 - is the x position of the blue cube
YPos2 - is the y position of the blue cube
Keys:
A - Go up for black cube
Z - Go down for black cube
K - Go up for blue cube
M - go down for blue cube
We have tried using a more complicated version which used j-label animation. How ever we want to make our pong game through the graphics function. But we do not understand:
import java.awt.*;
import javax.swing.*;
import java.awt.event.*;
public class PongHelpNeed extends JFrame implements KeyListener
{
// booleans to tell which key is pressed
boolean upKey;
boolean downKey;
boolean upKey2;
boolean downKey2;
// the position variables
int yPos;
int xPos;
int xPos2;
int yPos2;
public PongHelpNeed ()
{
//create window
super ("Controller");
setSize (660, 700);
// set keys to false and original positions
upKey = false;
downKey = false;
upKey2 = false;
downKey2 = false;
xPos = 100;
yPos = 350;
xPos2 = 500;
yPos2 = 350;
// add the frame as a listener to your keys
addKeyListener (this);
// Show the frame
setVisible(true);
}
//needs to be here because the class implements KeyListener
public void keyTyped (KeyEvent e)
{
System.out.println (e.getKeyCode () + " Typed");
}
//needs to be here because the class implements KeyListener
public void keyPressed (KeyEvent e) {
//check if keys a,z or k,m are pressed
if (e.getKeyCode () == KeyEvent.VK_A)
{
upKey = true;
}
else if (e.getKeyCode () == KeyEvent.VK_Z)
{
downKey = true;
}
else if (e.getKeyCode () == KeyEvent.VK_K)
{
upKey2 = true;
}
else if (e.getKeyCode () == KeyEvent.VK_M)
{
downKey2 = true;
}
//repaint the window everytime you press a key
repaint ();
}
//needs to be here because the class implements KeyListener
public void keyReleased (KeyEvent e)
{
System.out.println (e.getKeyCode () + " Released");
}
//paints the pictures
public void paint (Graphics g)
{
//set background
g.setColor(Color.WHITE);
g.fillRect(0, 0, 660, 700);
//cube 1
g.setColor(Color.BLACK);
g.fillRect(xPos,yPos,50, 50);
//draw cube 2
g.setColor(Color.BLUE);
g.fillRect(xPos2,yPos2, 50, 50);
//if keys are pressed move the cubes accordingly up or down
if (upKey == true)
{
yPos = yPos - 15;
upKey = false;
}
else if (downKey == true)
{
yPos = yPos + 15;
downKey = false;
}
else if (downKey2 == true){
yPos2 = yPos2 + 15;
downKey2 = false;
}
else if (upKey2 == true) {
yPos2 = yPos2 - 15;
upKey2 = false;
}
}
public static void main (String[] args)
{
new PongHelpNeed ();
}
}
Our expected results are we are trying to move both cube at the same time. So when we press the A key and the K key the black square should move and the blue cube should move.
Calling repaint() does not trigger a call to the paint immediately, so it's possible that the keyPressed is triggered twice (or more) before paint.
In your paint method you are checking the keys in if-else, which means that if one of the flags is true, the rest are not checked. You also have a race condition where the keyPressed is fighting with paint over the flags. Also, if you press a key quickly multiple times, you'll lose all the extra key presses between the first handled event and the next repaint.
Instead of doing the move within paint, you should do it within the keyPressed handler. Don't set a flag to e.g. upKey = true;, but instead do the action directly: yPos = yPos - 15;. The paint method will then just refresh the view to reflect the current (updated) state.

Why does my method for collision detection not work?

I understand the basis of collision detection and have developed my way (as far as I know) for collision detection. However, it does not seem to be working. What I have done is instead of drawing full rectangles around sprites, shapes, and other objects, I simply made methods that draw lines (very thin rectangles) on all sides of the object, left, right, top, and bottom.
The methods that draw the thin rectangles on the left,right,top, and bottom of the specified sprites. This is probably not the best way to do collision, but this is what I have, and am open to all ideas and different solutions!
Note: The(int)(x-Game.getCamera().getX()),(int)(y + height -Game.getCamera().getY()), is simply for the camera, so when the player moves, the trees (in my case the object I want to collide with) do not move with him
public Rectangle getBounds() {
return new Rectangle((int)(x-Game.getCamera().getX()),
(int)(y + height - Game.getCamera().getY()),width,height-height);
}
public Rectangle getBoundsTop() {
return new Rectangle((int)(x-Game.getCamera().getX()),
(int)(y- Game.getCamera().getY()),width,height-height);
}
public Rectangle getBoundsRight() {
return new Rectangle((int)(x + width -Game.getCamera().getX()),
(int)(y - Game.getCamera().getY()),width - width,height);
}
public Rectangle getBoundsLeft() {
return new Rectangle((int)(x -Game.getCamera().getX()),
(int)(y- Game.getCamera().getY()),width - width,height);
}
Actual collision CHECKING (in player class)-
if(getBounds().intersects(treeTile.getBoundsTop())) {
//What happens when they collide
//When the bottom of the player collides with the top of the tree.
}
if(getBoundsTop().intersects(treeTile.getBounds())) {
//What happens when they collide
//When the top of the player collides with the bottom of the tree
}
if(getBoundsRight().intersects(treeTile.getBoundsLeft())) {
//What happens when they collide
//when the right side of the player collides with the left of the
//tree
}
if(getBoundsLeft().intersects(treeTile.getBoundsRight())) {
//What happens when they collide
//When the left of the player collides with the right of the tree
}
I appreciate all the help I can get
Here is collision and movement code-
for (int i = 0; i < handler.object.size(); i++) {
Square handle = handler.object.get(i);
if (getBounds().intersects(treeTile.getBoundsTop())) {
handle.setvelY(-1);
}
if (getBoundsTop().intersects(treeTile.getBounds())) {
handle.setvelY(0);
}
if (getBoundsRight().intersects(treeTile.getBoundsLeft())) {
handle.setvelX(0);
}
if (getBoundsLeft().intersects(treeTile.getBoundsRight())) {
handle.setvelX(0);
}
}
Movement-
public void keyPressed(KeyEvent e) {
int code = e.getKeyCode();
for (int i = 0; i < handler.object.size(); i++) {
Square handle = handler.object.get(i);
if (handle.getId() == ObjectId.Zelda) {
if (code == KeyEvent.VK_D) {
handle.right();
}
if (code == KeyEvent.VK_A) {
handle.left();
}
if (code == KeyEvent.VK_W) {
handle.up();
}
if (code == KeyEvent.VK_S) {
handle.down();
}
}
My new collision idea -
boolean walkRight = true;
boolean walkLeft = true;
boolean walkDown = true;
boolean walkUp = true;
public void tick() {
if(walkRight == true) {
velX = 2;
}if(walkLeft == true) {
velX = -2;
}if(walkDown == true) {
velY = 2;
}if(walkUp == true) {
velY = -2;
}
if (getBounds().intersects(treeTile.getBoundsTop())) {
walkDown = false;
}
if (getBoundsTop().intersects(treeTile.getBounds())) {
walkUp = false;
}
if (getBoundsRight().intersects(treeTile.getBoundsLeft())) {
walkRight = false;
}
if (getBoundsLeft().intersects(treeTile.getBoundsRight())) {
walkLeft = false;
}
Something like this, not exactly, it is just an example.
Have you tried using 1 instead of width - width and height - height ?
I think these evaluating to 0 for the width / height is causing the intersects function to fail.
EDIT
I think you're over complicating this. If you just have one big collision box, instead of four small ones, then where ever you're moving your player, you can
// Square previousPosition = currentPosition
// do move player code, which presumably updates the currentPosition
// loop through all the trees you have
// if the player is colliding with a tree
// currentPosition = previousPosition
This should handle the player colliding with any side of the tree, since we just move them back to where they were previously.

Flicker on movement (Not using JPanel or Swing)

first post here so forgive me if this is the absolute wrong thing but I'm in a tiny bit of a pickle. - My computer science class has us making our own game/program from scratch, but our teacher specifically ignored the existence of swing and said that it was out of his realm to teach it so from my readings it seems as if a game is almost out of the question, with what I've written so far my main sprite and debugging information flickers violently on key input. Is there a way around this?
I know JPanel/JFrame/Swing would fix this, but we weren't taught it and all I've read so far is confusing me to no end, if someone would be able to help me convert my code to use it that would be amazing as I learn easier from dissecting it.. Which may seem a bit dodgy but it really is the easiest way to learn from. My code so far is;
public class GameThing extends Applet implements KeyListener {
Image rightsprite, picture;
Image leftsprite, picture2;
Image spritedead, picture3;
Image background, picture4;
boolean left;
boolean dead;
boolean wall;
boolean menu;
int widthX, heightY;
int bgx, bgy;
String sr = " ";
char ch = '*';
Graphics bufferGraphics;
Image offscreen;
Dimension dim;
#Override
public void init() {
rightsprite = getImage(getDocumentBase(), "SpriteRight.png");
leftsprite = getImage(getDocumentBase(), "SpriteLeft.png");
spritedead = getImage(getDocumentBase(), "Sprite.png");
background = getImage(getDocumentBase(), "Background.png");
requestFocus();
addKeyListener(this);
left = false;
menu = true;
setSize(600, 380);
dim = getSize();
offscreen = createImage(dim.width, dim.height);
bufferGraphics = offscreen.getGraphics();
bgx = 0;
bgy = 0;
}
public void pause(final int delay) { // PAUSE COMMAND LIFTED FROM RAY
// waits a while
try {
Thread.sleep(delay);
} catch (InterruptedException e) {
return;
}
}
public GameThing() { // SPRITE INITIAL SPAWN LOCATION
widthX = 100;
heightY = 300;
}
#Override
public void paint(final Graphics g) { // DRAW MAIN CHARACTER SPRITE AND BACKGROUND
bufferGraphics.setColor(Color.black); // DRAW GROUND (REPLACE WITH BLOCKS SOON)
bufferGraphics.drawRect(0, 334, 500, 50);
bufferGraphics.setColor(Color.green);
bufferGraphics.fillRect(0, 335, 500, 50);
g.drawImage(offscreen, 0, 0, this);
if (menu == true) {
pause(500);
g.drawString("Side Scroller Test.", 180, 250);
pause(1500);
menu = false;
}
if (left == false) { // LEFT KEY MOVEMENT COMMANDS
g.drawImage(rightsprite, widthX, heightY, this);
g.drawString("Sprites Current X Location is: " + widthX, 20, 30);
g.drawString("Sprites Current Y Location is: " + heightY, 20, 50);
g.drawString("Background X location is: " + bgx, 20, 90);
g.drawString("Currently: " + sr, 20, 70);
}
if (left == true) { // RIGHT KEY MOVEMENT COMMANDS
g.drawImage(leftsprite, widthX, heightY, this);
g.drawString("Sprites Current X Location is: " + widthX, 20, 30);
g.drawString("Sprites Current Y Location is: " + heightY, 20, 50);
g.drawString("Background X location is: " + bgx, 20, 90);
g.drawString("Currently: " + sr, 20, 70);
}
if (dead == true) { // COMMANDS TO EXECUTE WHEN MAIN CHARACTER DIES
widthX = 506;
heightY = 314;
g.drawImage(spritedead, widthX, heightY, this);
pause(500);
g.setColor(Color.white);
g.drawRect(widthX, heightY, widthX, heightY);
g.fillRect(widthX, heightY, widthX, heightY);
pause(500);
widthX = 100;
heightY = 300;
bgx = 0;
left = true;
dead = false;
}
}
#Override
public void update(final Graphics g) { // KEEPS BACKGROUND STATIC
paint(g);
}
public void keyPressed(final KeyEvent e) {
sr = "blank!";
ch = '1';
if (e.getKeyCode() == 39) {
sr = "Moving Right!";
widthX = widthX + 7;
if (widthX <= 121) {
bgx = bgx;
} else {
bgx = bgx - 7;
}
left = false;
if ((widthX > 490) && (heightY > 300)) { // FALL DEATH
sr = "You Died!";
widthX = 900;
heightY = 900;
dead = true;
}
if (widthX == 499) {
heightY = heightY + 7;
}
}
if (widthX == 2) {
wall = true;
}
if (e.getKeyCode() == 37) {
if (wall == true) {
sr = "Wall!";
if (widthX > 2) {
wall = false;
}
}
if (wall == false) {
sr = "Moving Left!";
widthX = widthX - 7;
if (bgx >= 0) {
bgx = bgx;
} else {
bgx = bgx + 7;
}
left = true;
}
}
if (e.getKeyCode() == 38) {
sr = "Jumping!";
}
repaint();
}
public void keyTyped(final KeyEvent e) {
ch = e.getKeyChar();
repaint();
}
public void keyReleased(final KeyEvent e) {
} // key released
}
ps: Jumping with the up key has been a problem too, I can't simply add to the Y variable and then subtract as it just goes so fast it's as if it never moved.. The basics of a sidescroller are a lot more than I ever expected..
I edited your code:
public class GameThing extends Applet {
// External resources
BufferedImage rightSprite, leftSprite, spriteDead, backgroundImg;
// Game data
int state = 1; //0 = left, 1 = right, 2 = dead
// Geometry
int locX = 100, locY = 300;
int bgX = 0, bgY = 0;
int groundX = 0, groundY = 334, groundW = 500, groundH = 50;
int appletW = 600, appletH = 480;
int wallW = 20, wallH = 40, wallX = 20, wallY = groundY - wallH;
final int STEP_SIZE = 7;
// Information
final String X_LOC_STR = "Sprites Current X Location is: ";
final String Y_LOC_STR = "Sprites Current Y Location is: ";
final String STATE_STR = "Currently: ";
final String X_BG_STR = "Background X location is: ";
String stateString = "";
// Double buffering
Image offscreen;
Graphics bufferGraphics;
Dimension dim;
// GUI components
Panel gamePanel;
Panel statusPanel = new Panel();
Label xLocLabel = new Label();
Label yLocLabel = new Label();
Label stateLabel = new Label();
Label xBGLocLabel = new Label();
#Override
public void init() {
// Load images
try {
rightSprite = ImageIO.read(new File(getClass().getResource("SpriteRIght.png").getPath()));
leftSprite = ImageIO.read(new File(getClass().getResource("SpriteLeft.png").getPath()));
spriteDead = ImageIO.read(new File(getClass().getResource("SpriteDead.png").getPath()));
backgroundImg = ImageIO.read(new File(getClass().getResource("Background.png").getPath()));
} catch (IOException e) {
e.printStackTrace();
}
// Set the panel that displays data
statusPanel.setLayout(new BoxLayout(statusPanel, BoxLayout.Y_AXIS));
statusPanel.add(xLocLabel);
statusPanel.add(yLocLabel);
statusPanel.add(stateLabel);
statusPanel.add(xBGLocLabel);
// Create the panel that draws the game and specify its behavior
gamePanel = new Panel() {
// Reduces flickering
#Override
public void update(Graphics g) {
paint(g);
}
// DRAW MAIN CHARACTER SPRITE AND BACKGROUND
#Override
public void paint(Graphics g) {
bufferGraphics.clearRect(0, 0, dim.width, dim.width);
bufferGraphics.drawImage(backgroundImg, bgX, bgY, this);
bufferGraphics.setColor(Color.BLACK); // DRAW GROUND (REPLACE WITH BLOCKS SOON)
bufferGraphics.drawRect(groundX, groundY, groundW, groundH);
bufferGraphics.setColor(Color.GREEN);
bufferGraphics.fillRect(groundX, groundY + 1, groundW, groundH);
bufferGraphics.setColor(Color.RED);
bufferGraphics.fillRect(wallX, wallY, wallW, wallH);
switch (state) {
case 0: bufferGraphics.drawImage(leftSprite, locX, locY, this);
break;
case 1: bufferGraphics.drawImage(rightSprite, locX, locY, this);
break;
case 2: bufferGraphics.drawImage(spriteDead, locX, locY, this);
// After death wait a bit and reset the game
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
pause(2000);
reset();
}
});
break;
}
g.drawImage(offscreen, 0, 0, this);
}
};
// Set the applet window
setLayout(new BorderLayout());
add(statusPanel, BorderLayout.PAGE_START);
add(gamePanel);
// Set double buffering
setSize(appletW, appletH);
dim = getSize();
offscreen = createImage(dim.width, dim.height);
bufferGraphics = offscreen.getGraphics();
// Set the panel that draws the game
gamePanel.addKeyListener(new Controls());
gamePanel.requestFocusInWindow();
updateLabels();
}
// PAUSE COMMAND LIFTED FROM RAY (who is Ray?)
// waits a while
public void pause(int delay) {
try {
Thread.sleep(delay);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// Set to start parameters
public void reset() {
locX = 100; locY = 300;
bgX = 0;
state = 1;
gamePanel.repaint(); // These 2 lines automatically restart the game
updateLabels(); // Remove if you want to stay on the death screen until next press
}
public void updateLabels() {
xLocLabel.setText(X_LOC_STR + locX);
yLocLabel.setText(Y_LOC_STR + locY);
stateLabel.setText(STATE_STR + stateString);
xBGLocLabel.setText(X_BG_STR + bgX);
}
private class Controls extends KeyAdapter {
public void keyPressed(KeyEvent e) {
switch (e.getKeyCode()) {
case KeyEvent.VK_RIGHT:
stateString = "Moving Right!";
locX += STEP_SIZE;
state = 1;
if (locX > 121)
bgX -= STEP_SIZE;
// FALL DEATH
if ((locX > 490) && (locY > 300)) {
stateString = "You Died!";
state = 2;
}
// Start fall
if (locX == 499)
locY += STEP_SIZE;
// Check wall collision
if (locX >= wallX && locX <= wallX + wallW) {
locX -= STEP_SIZE;
stateString = "Wall!";
}
break;
case KeyEvent.VK_LEFT:
stateString = "Moving Left!";
locX -= STEP_SIZE;
state = 0;
if (bgX < 0)
bgX += STEP_SIZE;
// Check wall collision
if (locX >= wallX && locX <= wallX + wallW) {
locX += STEP_SIZE;
stateString = "Wall!";
}
break;
case KeyEvent.VK_UP:
// Implement jumping
stateString = "Jumping!";
break;
}
// Repaint the game panel and update the data panel
gamePanel.repaint();
updateLabels();
}
}
}
Start by testing and understanding the changes:
I created a separate panel for the current data display and one for the game (which gets repainted).
I added a few help functions.
I made a lot of syntax changes (e.g. multiple if else replaced by switch, VK_UP instead of 37 and such).
Better to have all values in one place and read them from there instead of spreading them in various places (still needs a bit of work but it will change when advancing with the code).
Make constant values (values that don't change) final.
In Java conventions, constants are named with uppercase and underscore (_) (so Color.RED, not Color.red).
Better to use #Override when overriding methods even when the compiler understands it.
Do not block the event dispatch thread (EDT) with sleep or expensive computations. Methods like paint key events are meant to execute quickly. I used invokeLater to pause only after the EDT finished processing all queued events. (I understand that this threading subject comes as a bomb from nowhere, just bear with me).
Looking forward for the current state of the code (no new features):
state should probably be an enum or something close to it instead of random numbers.
Using Shape for drawn objects instead of specifying their parameters during painting.
Double buffering implementation notes:
You create an off-screen Image object offscreen and a Graphics object to draw on it - bufferGraphics. Insidepaint, you use only bufferGraphics for the drawings. You in fact draw on offscreen even though the paint belongs to gamePanel (or any other object and its paint), slightly confusing. When you finish drawing everything off-screen, you throw it all in one go onto the screen with g.drawImage(offscreen...) which uses gamePanel's Graphics object g.
The idea is like drawing everything on a paper and then putting it in front of the camera instead of drawing bit by bit on the camera, or something like that...
Currently, the off-screen image is the size of the whole applet and not just the game panel, but it makes little difference.
Relevant differences from a Swing approach:
Replace Label with JLabel, Panel with JPanel etc.
Swing components automatically implement double buffering (see JComponent's setDoubleBuffered and isDoubleBuffered). Remove all of those off-screen shenanigans and draw directly with Graphics g. No need to override update also.
Painting in Swing is done in paintComponent and not in paint.
Swing runs from its own thread and not from the EDT (not automatically, but easily done). There are some benefits to this I won't get into right now.
Key events are better handled by key bindings (Swing only) rather than the key listener. This manifests in responsiveness, native environment independence and more (e.g. concurrent key events).
#user1803551 Okay, so it's all sorted back to how it was again. - The scrolling background is in (Just a couple things around the place needed some tweaking from your post), and I understand that the final line "gamePanel.repaint(); is what's refreshing everything on the screen, since the background needs to be updated with every key input, it shouldn't be limited to the tiny square from your code, right? This now refreshes the whole screen, but I assume this isn't what is wanted because it just results in the flickering, the buffergraphics of the green rectangle at the bottom however don't flicker. So my assumption is to convert
g.drawImage(backgroundImg, bgx, bgy,this);
into buffergraphics?
-EDIT-
Moved the main Sprite into bufferGraphics aswell as the background image. Flickering is now almost gone, but after holding down an arrow key for a little bit the screen will flicker just for a moment and then return to normal.
public class GameTest extends Applet {
/**
*
*/
private static final long serialVersionUID = -1533028791433336859L;
Image rightsprite, leftsprite, spritedead, backgroundImg, offscreen;
boolean left = false, dead = false, wall = false, menu = true;
int locX = 100, locY = 300, width, height, bgx = 0, bgy = 0;
final int STEP_SIZE = 7;
final String X_LOC_STR= "Sprites Current X Location is: ";
final String Y_LOC_STR= "Sprites Current Y Location is: ";
final String STATE_STR= "Currently: ";
final String X_BG_STR= "Background X location is: ";
String sr = " ";
Graphics bufferGraphics;
Dimension dim;
Panel gamePanel;
Panel statusPanel = new Panel();
Label xLoc = new Label(X_LOC_STR);
Label yLoc = new Label(Y_LOC_STR);
Label state = new Label(STATE_STR);
Label xBGLoc = new Label(X_BG_STR);
public void init() {
try {
rightsprite = ImageIO.read(new File(getClass().getResource("SpriteRight.png").getPath()));
leftsprite = ImageIO.read(new File(getClass().getResource("SpriteLeft.png").getPath()));
spritedead = ImageIO.read(new File(getClass().getResource("SpriteDead.png").getPath()));
backgroundImg = ImageIO.read(new File(getClass().getResource("Background.png").getPath()));
} catch (IOException e) {
e.printStackTrace();
}
height = rightsprite.getHeight(this);
width = rightsprite.getWidth(this);
statusPanel.setLayout(new BoxLayout(statusPanel, BoxLayout.Y_AXIS));
statusPanel.add(xLoc);
statusPanel.add(yLoc);
statusPanel.add(state);
statusPanel.add(xBGLoc);
gamePanel = new Panel() {
/**
*
*/
private static final long serialVersionUID = 1L;
// DRAW MAIN CHARACTER SPRITE AND BACKGROUND
#Override
public void paint(Graphics g) {
bufferGraphics.drawImage(backgroundImg,bgx, bgy, this);
bufferGraphics.setColor(Color.black); // DRAW GROUND (REPLACE WITH BLOCKS SOON)
bufferGraphics.drawRect(0, 334, 500, 50);
bufferGraphics.setColor(Color.green);
bufferGraphics.fillRect(0, 335, 500, 50);
if (menu == true) {
pause(500);
g.drawString("Side Scroller Test.", 180, 250);
pause(1500);
menu = false;
}
// LEFT KEY MOVEMENT COMMANDS
if (left == false)
bufferGraphics.drawImage(rightsprite, locX, locY, this);
// RIGHT KEY MOVEMENT COMMANDS
else
bufferGraphics.drawImage(leftsprite, locX, locY, this);
// COMMANDS TO EXECUTE WHEN MAIN CHARACTER DIES
if (dead == true) {
locX = 506;
locY = 314;
bufferGraphics.drawImage(spritedead, locX, locY, this);
pause(500);
bufferGraphics.setColor(Color.white);
bufferGraphics.drawRect(locX, locY, width, height);
bufferGraphics.fillRect(locX, locY, width, height);
pause(500);
locX = 100;
locY = 300;
bgx = 0;
bgy = 0;
bufferGraphics.clearRect(locX, locY, width, height);
left = true;
dead = false;
}
g.drawImage(offscreen, 0, 0, this);
super.paint(g);
}
};
setLayout(new BorderLayout());
add(statusPanel, BorderLayout.PAGE_START);
add(gamePanel);
setSize(600, 480);
dim = getSize();
offscreen = createImage(dim.width, dim.height);
bufferGraphics = offscreen.getGraphics();
gamePanel.addKeyListener(new Controls());
gamePanel.requestFocusInWindow();
}
// waits a while
public void pause(int delay) {
try {
Thread.sleep(delay);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
private class Controls extends KeyAdapter {
public void keyPressed(KeyEvent e) {
sr = "blank!";
if (e.getKeyCode() == KeyEvent.VK_RIGHT) {
sr = "Moving Right!";
locX += STEP_SIZE;
if (locX > 121)
bgx = bgx - STEP_SIZE;
left = false;
// FALL DEATH
if ((locX > 490) && (locY > 300)) {
sr = "You Died!";
locX = 900;
locY = 900;
dead = true;
gamePanel.repaint();
}
if (locX == 499) {
locY += STEP_SIZE;
}
}
if (locX == 2) {
wall = true;
}
else if (e.getKeyCode() == KeyEvent.VK_LEFT) {
if (wall == true) {
sr = "Wall!";
if (locX > 2) {
wall = false;
}
}
else {
sr = "Moving Left!";
locX -= STEP_SIZE;
if (bgx < 0)
bgx += STEP_SIZE;
left = true;
}
}
if (e.getKeyCode() == KeyEvent.VK_UP) {
sr = "Jumping!";
}
xLoc.setText(X_LOC_STR + locX);
yLoc.setText(Y_LOC_STR + locY);
state.setText(STATE_STR + sr);
xBGLoc.setText(X_BG_STR + bgx);
gamePanel.repaint();
}
}
}

Categories