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();
}
}
}
Related
I'm playing around with graphics in Java. At the moment I have a rectangle that moves from left to right. I want it to start moving left once it hits the right side of the Canvas and left when it hits the right side, i have included a game loop as this will eventually turn into my first very basic game. Thanks.
P.S - I followed some tutorials for different parts of this code hence why it might be a bit messy, I'm working on it :)
Main Class:
public class Game extends JFrame implements Runnable {
private Canvas canvas = new Canvas();
private RenderHandler renderer;
private boolean running = true;
public static int WIDTH = 1200, HEIGHT = WIDTH / 12*9;
public static int moveX =WIDTH/2;
public Game() {
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setSize(WIDTH, HEIGHT);
setLocationRelativeTo(null);
setLocationRelativeTo(null);
add(canvas);
setVisible(true);
canvas.createBufferStrategy(3);
renderer = new RenderHandler(getWidth(), getHeight());
}
public void update() {
}
public void render() {
BufferStrategy bufferStrategy = canvas.getBufferStrategy();
Graphics graphics = bufferStrategy.getDrawGraphics();
super.paint(graphics);
renderer.render(graphics);
graphics.dispose();
bufferStrategy.show();
}
public void run() {
BufferStrategy bufferStrategy = canvas.getBufferStrategy();
int FRAMES = 0;
int TICKS = 0;
long lastTime = System.nanoTime();
double unprocessed = 0;
double nsPerSecs = 1000000000 /60.0;
long Timer = System.currentTimeMillis();
while(running) {
long now = System.nanoTime();
unprocessed += (now - lastTime) / nsPerSecs;
lastTime = now;
if(unprocessed >= 1) {
TICKS ++;
update();
unprocessed -= 1;
}
try
{
Thread.sleep(3);
}catch (InterruptedException e) {
e.printStackTrace();
}
FRAMES++;
render();
if(System.currentTimeMillis() - Timer > 1000) {
System.out.println("Ticks: " + TICKS + " FPS: " + FRAMES);
TICKS = 0;
FRAMES = 0;
Timer += 1000;
}
}
}
public static void main(String[] args) {
Game game = new Game();
Thread gameThread = new Thread(game);
gameThread.start();
}
}
Class drawing the graphics:
public class RenderHandler {
public RenderHandler(int width, int height) {
}
public void render(Graphics g) {
g.setColor(Color.BLACK);
g.fillRect(0, 0, Game.WIDTH, Game.HEIGHT);
g.setColor(Color.RED);
g.fillRect(Game.moveX, Game.HEIGHT/2, 50, 50);
if (Game.moveX >= Game.WIDTH) {
Game.moveX ++;
} else if (Game.moveX <= 0) {
Game.moveX --;
}else { Game.moveX++;
}
}
}
If you know how to draw on the screen and how stuffs work, I would think that this is more about getting the logic down.
This code slice I so brutally tore from your question is right next to where the rendering takes place (a problem because I view it as rather unorganized; I would recommend game logic and rendering to take place in two different functions). It basically says that it will move right if it is beyond the right of the screen, if not, it will move left if it is beyond the left of the screen, and finally, if not, it will just move left.
if (Game.moveX >= Game.WIDTH) {
Game.moveX ++;
} else if (Game.moveX <= 0) {
Game.moveX --;
}else { Game.moveX++;
}
If you want it to bounce, you will have to use a boolean to keep track of its moving state, or, if you want more versatility, use a pair of floats or doubles (floats are typically used in Java game design) to keep track of its position, and another for its velocity. I'm in a tight squeeze right now, I will return.
Add this to render handler instead of the current if statement in render
bool rol = true; // initialize this outside the method
If(Game.movex + 50 >= Game.width)
rol = false;
Else if(Game.movex <= 0)
rol = true;
If(rol)
Game.movex++;
Else
Game.movex--;
You need to store current moving direction somewhere, so add this to Game class:
public static int deltaX = 1;
And replace condition in render() with
if (Game.moveX >= Game.WIDTH-50) {
Game.deltaX =-1;
} else if (Game.moveX <= 0) {
Game.deltaX =1;
}
Game.moveX += Game.deltaX;
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.
Im having difficulty drawing a Sub Image of a Buffered Image everytime the Mouse Pointer Location equals that of the each border of the JPanel. The problem is that the BufferedImage that is equals the SubImage wont display
Here is the JPanel the initialization might not be correct Im still learning the components of Java and 2D graphics.
public class Map extends JPanel implements MouseListener, MouseMotionListener {
private final int SCR_W = 800;
private final int SCR_H = 600;
private int x;
private int y;
private int dx;
private int dy;
String dir = "C:\\imgs\\war\\";
private BufferedImage map_buffer;
public BufferedImage scr_buffer;
public void initScreen(int x, int y, int stage){
if(stage == 0){
try{ map_buffer = ImageIO.read(new File(dir + "map" + stage + ".jpg" ));
}catch(Exception error) { System.out.println("Error: cannot read tileset image.");
}
}
scr_buffer = map_buffer.getSubimage(x, y, SCR_W, SCR_H);
}
#Override
protected void paintComponent(Graphics g)
{
super.paintComponent(g);
if(scr_buffer == null)
initScreen(x, y, 0);
g.drawImage(scr_buffer, 0, 0, this);
}
boolean isLeftBorder = false;
boolean isRightBorder = false;
boolean isTopBorder = false;
boolean isBottomBorder = false;
public Map(){
addMouseListener(new MouseAdapter() {
public void mouseMoved(MouseEvent e) {
/**
* Check location of mouse pointer if(specified_edge)move(scr_buffer)
*
*/
System.out.println("MouseMove: " + e.getPoint().getX() + " , " + e.getPoint().getY());
if(e.getPoint().getX() == SCR_W)isRightBorder = true;
if(e.getPoint().getY() == SCR_H)isBottomBorder = true;
if(e.getPoint().getX() == 0 && e.getPoint().getY() == SCR_H)isLeftBorder = true;
if(e.getPoint().getY() == 0 && e.getPoint().getX() == SCR_W)isTopBorder = true;
if(e.getPoint().getX() != 0 && e.getPoint().getX() != SCR_W
&& e.getPoint().getY() != 0 && e.getPoint().getY() != SCR_H){
isLeftBorder = false;
isRightBorder = false;
isTopBorder = false;
isBottomBorder = false;
}
if(isRightBorder){ x += 2; repaint(); }
if(isBottomBorder){ y -= 2; repaint(); }
if(isLeftBorder){ x -= 2; repaint();}
if(isTopBorder){ y += 2; repaint(); }
}
});
}
}
In the main I init a JFrame to contain the Panel all im getting is a error
public static void main(String[] args) {
JFrame f = new JFrame("War");
f.setSize(800, 600);
f.setLayout(null);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Map m = new Map();
f.getContentPane().add(f);
f.setVisible(true);
}
In order to detect mouse movement you should use a MosuseMotionListener, while technically MouseAdapter implements this, you need to register it with the JPanel correctly
Instead of using addMouseListener, you'll want to use addMouseMotionListener instead
I'd also be worried about the use of SRC_W and SRC_H, as you can't guarantee the size of the panel. Instead, you should be using getWidth and getHeight, which will tell you the actual size of the component
You can improve the chances of obtaining the size you want by overriding the getPreferredSize and return the size you would like. You'd then use pack on the frame to wrap the frame about it
f.getContentPane().add(f); is adding the frame to itself, it should probably be more like f.getContentPane().add(m);
f.setLayout(null); will prevent any of the child components from been sized and positioned and is best avoid, just get rid of it.
Avoid using null layouts, pixel perfect layouts are an illusion within modern ui design. There are too many factors which affect the individual size of components, none of which you can control. Swing was designed to work with layout managers at the core, discarding these will lead to no end of issues and problems that you will spend more and more time trying to rectify
This scr_buffer = map_buffer.getSubimage(x, y, SCR_W, SCR_H); is also a little dangerous, as it could be asking for more of the image then is available, you should be testing to see if x + SCR_W < image width (and same goes for the height)
I don't know if this deliberate or not, but you never reset the "border" flags, so once set, they will always be true...
addMouseMotionListener(new MouseAdapter() {
public void mouseMoved(MouseEvent e) {
/**
* Check location of mouse pointer if(specified_edge)move(scr_buffer)
*
*/
isRightBorder = false;
isBottomBorder = false;
isTopBorder = false;
isLeftBorder = false;
You may also want to have a "space" around the edge, which when the mouse enters it, it will set the border flags, for example...
if (e.getPoint().getX() >= getWidth() - 4) {
isRightBorder = true;
}
if (e.getPoint().getY() >= getHeight() - 4) {
isBottomBorder = true;
}
if (e.getPoint().getX() <= 4) {
isLeftBorder = true;
}
if (e.getPoint().getY() <= 4) {
isTopBorder = true;
}
Your logic for the vertical movement is wrong, when the mouse is within the bottom border, it should add to the y position and subtract when it's within the top border...
if (isBottomBorder) {
y += 2;
}
if (isTopBorder) {
y -= 2;
}
You need to perform some range checking after you've modified the x/y positions to make sure you're not request for a portion of the image which is not available...
if (x < 0) {
x = 0;
} else if (x + getWidth() > map_buffer.getWidth()) {
x = map_buffer.getWidth() - getWidth();
}
if (y < 0) {
y = 0;
} else if (y + getHeight() > map_buffer.getHeight()) {
y = map_buffer.getHeight() - getHeight();
}
There is a logic error within the initScreen method, src_buffer is never set to null, meaning that once it has a "sub image", it never tries to obtain a new one (also, you shouldn't be loading the map_buffer in there either).
scr_buffer = null;
repaint();
Thank you for you time and understanding.
Inside mouse moved
if (e.getPoint().getX() >= getWidth() - 4) {
isRightBorder = true; // unnecessary
scr_buffer = null;
x = x + 2;
repaint();
}
So I followed a free manual instructing how to write Asteroids in Java, and it runs great. I would like to add it into a program so I can call it, run it for a time, and then have it return an int. The program I'm making is an application, and I've been searching for a while trying and failing, figuring out how to make make it run as an application. The stuff I've read here says I should do this:
public class Main extends JFrame {
public static void main(String[] args) {
AsteroidsGame game = new AsteroidsGame();
JFrame frame = new JFrame();
frame.setLayout(new GridLayout (1,1));
frame.add(game);
frame.setSize(500,500);
game.init();
game.start();
frame.setVisible(true);
}
}
But then I get an error
Exception in thread "main" java.lang.NullPointerException
at AsteroidsGame.init(AsteroidsGame.java:49)
at Main.main(Main.java:15)
which points me to the below for line 49, and my game.init() for line 15.
g = img.getGraphics();
Here's the code for the AsteroidsGame, I'm too tired to think anymore. I just need to get it to run as an application, any other code changes are irrelevant at this point. I know it's a lot of code but I'm hoping it's a simple something I've missed. Any help is greatly appreciated. Thank you!
import java.applet.*;
import java.awt.*;
import java.awt.event.*;
import java.util.Random;
public class AsteroidsGame extends Applet implements Runnable, KeyListener {
Thread thread;
Dimension dim;
Image img;
Graphics g;
long endTime, startTime, framePeriod;
Ship ship;
boolean paused; // True if the game is paused. Enter is the pause key
Shot[] shots;
int numShots;
boolean shooting;
Asteroid[] asteroids; // the array of asteroids
int numAsteroids; // the number of asteroids currently in the array
double astRadius, minAstVel, maxAstVel; // values used to create
// asteroids
int astNumHits, astNumSplit;
int level; // the current level number
int money;
double fuel;
Random rand = new Random();
public void init() {
resize(500, 500);
shots = new Shot[41]; // 41 is a shot's life period plus one.
// since at most one shot can be fired per frame,
// there will never be more than 41 shots if each one only
// lives for 40 frames.
numAsteroids = 0;
level = rand.nextInt(25)+1; // will be incremented to 1 when first level is set up
astRadius = 10; // values used to create the asteroids
minAstVel = 2;
maxAstVel = 8;
astNumHits = 3;
astNumSplit = 2;
money = 0;
fuel = 100;
endTime = 0;
startTime = 0;
framePeriod = 25;
addKeyListener(this); // tell it to listen for KeyEvents
dim = getSize();
img = createImage(dim.width, dim.height);
g = img.getGraphics();
thread = new Thread(this);
thread.start();
}
public void setUpNextLevel() { // start new level with one more asteroid
level++;
// create a new, inactive ship centered on the screen
// I like .35 for acceleration, .98 for velocityDecay, and
// .1 for rotationalSpeed. They give the controls a nice feel.
ship = new Ship(250, 250, 0, .35, .98, .1, 12);
numShots = 0; // no shots on the screen at beginning of level
paused = false;
shooting = false;
// create an array large enough to hold the biggest number
// of asteroids possible on this level (plus one because
// the split asteroids are created first, then the original
// one is deleted). The level number is equal to the
// number of asteroids at it's start.
asteroids = new Asteroid[level
* (int) Math.pow(astNumSplit, astNumHits - 1) + 1];
numAsteroids = level;
// create asteroids in random spots on the screen
for (int i = 0; i < numAsteroids; i++)
asteroids[i] = new Asteroid(Math.random() * dim.width,
Math.random() * dim.height, astRadius, minAstVel,
maxAstVel, astNumHits, astNumSplit);
}
public void paint(Graphics gfx) {
g.setColor(Color.black);
g.fillRect(0, 0, 500, 500);
for (int i = 0; i < numShots; i++)
// draw all the shots on the screen
shots[i].draw(g);
for (int i = 0; i < numAsteroids; i++)
asteroids[i].draw(g);
ship.draw(g); // draw the ship
g.setColor(Color.cyan); // Display level number in top left corner
g.drawString("Level " + level, 20, 20);
g.drawString("Money " + money, 80, 20);
g.drawString("Fuel " + fuel, 20, 50);
gfx.drawImage(img, 0, 0, this);
}
public void update(Graphics gfx) {
paint(gfx);
}
public void run() {
for (;;) {
startTime = System.currentTimeMillis();
// start next level when all asteroids are destroyed
if (numAsteroids <= 0)
setUpNextLevel();
if (!paused) {
ship.move(dim.width, dim.height); // move the ship
// move shots and remove dead shots
for (int i = 0; i < numShots; i++) {
shots[i].move(dim.width, dim.height);
// removes shot if it has gone for too long
// without hitting anything
if (shots[i].getLifeLeft() <= 0) {
// shifts all the next shots up one
// space in the array
deleteShot(i);
i--; // move the outer loop back one so
// the shot shifted up is not skipped
}
}
if(ship.accelerating && fuel>0){
fuel -= 1.5;
}
if(ship.accelerating && fuel == 0)
{
ship.accelerating = false;
}
// move asteroids and check for collisions
updateAsteroids();
if (shooting && ship.canShoot()) {
// add a shot on to the array
shots[numShots] = ship.shoot();
numShots++;
}
}
repaint();
try {
endTime = System.currentTimeMillis();
if (framePeriod - (endTime - startTime) > 0)
Thread.sleep(framePeriod - (endTime - startTime));
} catch (InterruptedException e) {
}
}
}
private void deleteShot(int index) {
// delete shot and move all shots after it up in the array
numShots--;
for (int i = index; i < numShots; i++)
shots[i] = shots[i + 1];
shots[numShots] = null;
}
private void deleteAsteroid(int index) {
// delete asteroid and shift ones after it up in the array
numAsteroids--;
for (int i = index; i < numAsteroids; i++)
asteroids[i] = asteroids[i + 1];
asteroids[numAsteroids] = null;
}
private void addAsteroid(Asteroid ast) {
// adds the asteroid passed in to the end of the array
asteroids[numAsteroids] = ast;
numAsteroids++;
}
private void updateAsteroids() {
for (int i = 0; i < numAsteroids; i++) {
// move each asteroid
asteroids[i].move(dim.width, dim.height);
// check for collisions with the ship, restart the
// level if the ship gets hit
if (asteroids[i].shipCollision(ship)) {
money+= Math.random()*1000;
deleteAsteroid(i);
//level--; // restart this level
//numAsteroids = 0;
return;
}
// check for collisions with any of the shots
for (int j = 0; j < numShots; j++) {
if (asteroids[i].shotCollision(shots[j])) {
// if the shot hit an asteroid, delete the shot
deleteShot(j);
// split the asteroid up if needed
if (asteroids[i].getHitsLeft() > 1) {
for (int k = 0; k < asteroids[i].getNumSplit(); k++)
addAsteroid(asteroids[i].createSplitAsteroid(
minAstVel, maxAstVel));
}
// delete the original asteroid
deleteAsteroid(i);
j = numShots; // break out of inner loop - it has
// already been hit, don't need to check
// for collision with other shots
i--; // don't skip asteroid shifted back into
// the deleted asteroid's position
}
}
}
}
public void keyPressed(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_ENTER) {
// These first two lines allow the asteroids to move
// while the player chooses when to enter the game.
// This happens when the player is starting a new life.
if (!ship.isActive() && !paused)
ship.setActive(true);
else {
paused = !paused; // enter is the pause button
if (paused) // grays out the ship if paused
ship.setActive(false);
else
ship.setActive(true);
}
} else if (paused || !ship.isActive()) // if the game is
return; // paused or ship is inactive, do not respond
// to the controls except for enter to unpause
else if (e.getKeyCode() == KeyEvent.VK_UP)
{if(fuel>0){
ship.setAccelerating(true);
//fuel -= 1.5;
}
else{
fuel = 0;
}
}
else if (e.getKeyCode() == KeyEvent.VK_LEFT)
{
ship.setTurningLeft(true);
}
else if (e.getKeyCode() == KeyEvent.VK_RIGHT)
ship.setTurningRight(true);
//else if (e.getKeyCode() == KeyEvent.VK_CONTROL)
//shooting = true; // Start shooting if ctrl is pushed
}
public void keyReleased(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_UP)
ship.setAccelerating(false);
else if (e.getKeyCode() == KeyEvent.VK_LEFT)
ship.setTurningLeft(false);
else if (e.getKeyCode() == KeyEvent.VK_RIGHT)
ship.setTurningRight(false);
//else if (e.getKeyCode() == KeyEvent.VK_CONTROL)
//shooting = false;
}
public void keyTyped(KeyEvent e) {
}
}
I believe that createImage() is going to return null until the component is actually on-screen, wihch i believe is after the thread has started.
Instead of this:
img = createImage(dim.width, dim.height);
g = img.getGraphics();
thread = new Thread(this);
thread.start();
try:
new Thread(new Runnable() {
public void run() {
do {
try {
Thread.sleep(25);
} catch(InterruptedException e) {
System.out.println("wait interrupted");
break;
}
} while(!isDisplayable());
// Now it should be safe to make these calls.
img= createImage(dim.width, dim.height);
g = img.getGraphics();
start();
}
}).start();
it seems to be very stupid question, but I really need a help, I'm making a task with drawing a background transparent gradient image and then draw some objects over it, the problem is I want to draw this image once but the other objects will be drawn multi time to perform some animation
the code is as following, this is the code that i want to run once, and I have created a boolen variable = false and then set it to true
public void drawLockLayer(Graphics g) {
try {
lock = Image.createImage(Paths.lock);
g.drawImage(lock, 0, 0, LGBMainMidlet.width, LGBMainMidlet.height);
System.out.println("After Draw Image");
drawOnce = true;
} catch (Exception ex) {
ex.printStackTrace();
}
}
other code is as following
public void paint(Graphics g, Rectangle rect) {
synchronized (g) {
if (drawOnce == false) {
drawLockLayer(g);
}
}
pos = (int) ((System.currentTimeMillis() % 2700) / 300);
int i = 0;
g.setColor(bgColor);
g.fillRoundRect(startX, startY, width, height, 20, 20);
g.setColor(fgColor);
g.setFont(font);
g.drawString(loadMsg, startX + (spacing / 4), startY + (spacing / 4));
for (int y = 0; y < 3; y++) {
for (int x = 0; x < 3; x++) {
int thickness = 6;
if (i == pos) {
thickness = 14;
} else if (i == pos - 1) {
thickness = 10;
}
g.fillRect((startX + x * spacing - (thickness / 2)) + (width / 3), (startY + y * spacing - (thickness / 2)) + (height / 3), thickness, thickness);
i++;
}
}
}
it is enter the method but it is not draw the background image , What I want to do is to lock the graphics object until he finish drawing the image then continue with other code
can anyone help please
Several issues with the code:
synchronizing on a Graphics is generally a bad idea
paint is only supposed to be called from one thread. are you sure it isn't and you actually need to synchronize? where does rect come from?
to make it easier to maintain, you should set drawOnce to true in paint()
You need to set drawOnce to false before calling paint.
It looks like your code is based on GameCanvas. What do you think of the below approach?
class MainCanvas extends GameCanvas {
// rect is an attribute
private void updateScreen() {
drawOnce = false;
paint(getGraphics(), rect);
}
public void start() {
new DataThread().start();
new AnimationThread().start();
}
class DataThread extends Thread {
public void run() {
while (/*IO stuff not done*/) {
// save data
updateScreen();
}
}
}
class AnimationThread extends Thread {
public void run() {
while (/*IO stuff not done*/) {
// sleep a little
updateScreen();
}
}
}
// drawLockLayer and paint(Graphics, Rectangle) methods
} // end of MainCanvas class