Currently I am double buffering in my program(though you should know) and I have an array-list that stores BufferedImages, and Integers. Currently my code to move the whole arraylist across the screen is:
public static void MoveMap(final double ammount){
Thread t = new Thread(){
public void run(){
for(int i = 0; i < TileHelper.MAIN_LIST.size();i++){
TileHelper.MAIN_LIST.get(i).changeX(ammount*DATA.speed);
}
}
};
t.start();
}
This gets the job done but it doesn't look smooth. Even if I use a number like .25 for the variable amount.(I just noticed I spelt it wrong in my code :O) How do I make nice smooth movement if I have all of my images in this array? Btw If you want to know what isn't smooth with this, it's the fact that this lags and between each image you see a big white space. Any help is apreciated... Thank you!
Rendering code:
public class Main extends JFrame {
public static int worldsize = 1080;
private static final long serialVersionUID = 6149918602875295087L;
public static void main(String args[]){
new Main();
}
TileHelper tiles = new TileHelper(TileHelper.MAIN_LIST);
public Main(){
DATA_IMAGES.initImages();
addDefaultBlocks();
setSize(640,480);
setResizable(false);
setTitle("Stranded");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setLocationRelativeTo(null);
setVisible(true);
createBufferStrategy(2);
GameLoop();
addKeyListener(new KeyListener() {
public void keyTyped(KeyEvent e) {
}
public void keyReleased(KeyEvent e) {
}
public void keyPressed(KeyEvent e) {
int key = e.getKeyCode();
switch(key){
case KeyEvent.VK_LEFT:
Movement.MoveMap(3.25);
break;
case KeyEvent.VK_RIGHT:
Movement.MoveMap(-3.25);
break;
case KeyEvent.VK_UP:
DATA.speed++;
break;
case KeyEvent.VK_DOWN:
DATA.speed--;
break;
case KeyEvent.VK_SPACE:
System.out.println(DATA.speed);
break;
}
}
});
}
private void addDefaultBlocks() {
for(int x = -1000/32; x < worldsize/32; x++){
for (int y = -1000/32; y < worldsize/32; y++){
TileHelper.addBlock(DATA_IMAGES.GRASS, x*32, y*32);
}
}
}
public void GameLoop(){
Thread gameloop = new Thread(){
public void run(){
while(true){
RenderInit();
try {
Thread.sleep(30);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
};
gameloop.start();
}
public void RenderInit(){
BufferStrategy buff = getBufferStrategy();
Graphics g = buff.getDrawGraphics();
g.clearRect(0, 0, getWidth(), getHeight());
Render(g);
g.dispose();
buff.show();
Toolkit.getDefaultToolkit().sync();
}
public void Render(Graphics g){
tiles.RenderAll(g);
}
}
I'm assuming you're attempting to make a sidescroller game, and the way I got this to work was to make all objects in the games, a sprite.
I created a Sprite class which contains, x, y, xvelocity, yvelocity attributes as well as getters and setters. When I wanted to scroll, I either set the velocity to the opposite direction temporarily, or moved the x value.
Another method was to keep the original x and y values stored in each sprite, and when drawing to the screen I had a variable called: "offSetX" which was added or subtracted by the sprite's original position to have a unified movement between all sprites on screen.
If this is not what you're looking for, i'll delete this. :)
Note:
I recently modified my old sidescroller game engine and found the last method as the best one, rather than modifying all the x positions on the sprite list. My offSetX variable is a float value and when drawing to the screen I use Math.round(sprite.getX() + offSetX) to get the most accurate position.
Related
My paint method doesnt seem to paint my 20x20 cells. I have a boolean array for the cells to control their state and that if true, call the cells paint method, a cell is painted however I have two problems;
Only one is painted at a time which is odd because i should have a 40x40 array of booleans meaning i have 40x40 cells
They dont actually paint exactly where I click. I do not know how this is the case as when I get the co-ordinates of my click I immediately place those co-ordinates as my x, and y values in my paint method.
Main
import javax.swing.*;
import java.awt.*;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.image.BufferStrategy;
public class mainApplication extends JFrame implements Runnable, MouseListener {
private static final Dimension windowsize = new Dimension(80, 600);
private BufferStrategy strategy;
private Graphics offscreenGraphics;
private static boolean isGraphicsInitialised = false;
private static int rows = 40;
private static int columns = 40;
private static int height = windowsize.height;
private static int width = windowsize.width;
private static Cells cells = new Cells();
private int xArrayElement,yArrayElement, xPosition, yPosition;
private static boolean gameState[][] = new boolean[rows][columns];
public mainApplication() {
System.out.println(System.getProperty("user.dir"));
setDefaultCloseOperation(EXIT_ON_CLOSE);
Dimension screensize = java.awt.Toolkit.getDefaultToolkit().getScreenSize();
int x = screensize.width / 2 - windowsize.width / 2;
int y = screensize.height / 2 - windowsize.height / 2;
setBounds(x, y, screensize.width, screensize.height);
setVisible(true);
createBufferStrategy(2);
strategy = getBufferStrategy();
offscreenGraphics = strategy.getDrawGraphics();
isGraphicsInitialised = true;
// MouseEvent mouseEvent = new MouseEvent();
addMouseListener(this);
// addMouseMotionListener(MouseEvent);
Thread t = new Thread(this);
t.start();
}
public void mousePressed(MouseEvent e) { }
public void mouseReleased(MouseEvent e) { }
public void mouseEntered(MouseEvent e) { }
public void mouseExited(MouseEvent e) { }
public void mouseClicked(MouseEvent e) {
if(e.getClickCount() == 1){
xPosition = e.getX();
yPosition = e.getY();
cells.setPosition(xPosition,yPosition);
xArrayElement = (xPosition/20);
yArrayElement = (yPosition/20);
if(gameState[xArrayElement][yArrayElement]){
gameState[xArrayElement][yArrayElement] = false;
}
else if (!gameState[xArrayElement][yArrayElement]) {
gameState[xArrayElement][yArrayElement] = true;
}
else(gameState[xArrayElement][yArrayElement]) = true;
}
}
#Override
public void run() {
while (true) {
try { //threads entry point
Thread.sleep(20); //forces us to catch exception
}
catch (InterruptedException e) {
}
this.repaint();
}
}
public void paint(Graphics g) {
if (isGraphicsInitialised) {
g = strategy.getDrawGraphics();
g.setColor(Color.BLACK);
g.fillRect(0, 0, 800, 800);
if (gameState[xArrayElement][yArrayElement]) {
g.setColor(Color.WHITE);
cells.paint(g);
System.out.println(xPosition);
}
else if (!gameState[xArrayElement][yArrayElement]) {
g.setColor(Color.BLACK);
g.fillRect(xPosition, yPosition, 20, 20);
}
strategy.show();
}
}
public static void main(String[]args){
mainApplication test = new mainApplication();
}
}
Cell Class
import java.awt.*;
public class Cells {
int x;
int y;
public Cells(){
}
public void setPosition(int xi, int xj){
x = xi;
y = xi;
}
public boolean cellState(boolean visible){
return visible;
}
public void paint(Graphics g){
g.drawRect(x, y, 20,20);
}
}
You are doing a number of things wrong. My first suggestion would be to forget about offscreen graphics and ensure you are doing what you want. You can always create an image latter. Here are some basic guidelines:
Don't extend JFrame. Use an instance.
Extend JPanel or create a class that extends JPanel and add to frame instance
Then override paintComponent(g) and use that graphics context to draw.
Here is an earlier answer that may help Can't add Graphics into JPanel in Java
More information may be found in the Java Tutorials on painting.
Updated. It took me a few minutes to find this.
public void setPosition(int xi, int xj){
x = xi;
y = xi; // <--- should be xj
}
Regarding (1) above. You must repaint every cell each time you enter paintComponent. This means you will need to iterate across the list and paint them in the correct spot. Right now you are only painting one upon each entry.
A couple more suggestions. Instead of messing with the thread and calling repaint every 20ms in a loop, why not just invoke repaint in the mouseClicked() method.
If you do eventually need to paint every 20ms. I suggest using a swing Timer as follows: (check JavaDoc to ensure I got the syntax correct!!)
Timer timer = new Timer(0, (ev)-> frame.repaint());
timer.setDelay(20);
timer.start();
And you can create your own mouseListener class and extending MouseAdapter. The purpose of these adapter classes is to keep the clutter down so you don't have to have empty methods to satisfy the interface requirements. Put the class inside your main class so it has access to the appropriate data structures. Then just add an instance of it to the mouse listener of the target Component.
I'm building my first very basic game in JAVA without using any other external libraries.
The problem I got is
You can see that the background is rendered over player how can I fix that
The player rendering code is as follows
#Override
public void render(Graphics g) {
int bulletcount = 0;
//render player
g.setColor(color);
g.fillRect(x,y,32,32);
if(shooting) {
handler.add(new bullet(x + 8, y - 24, ID.bullet, handler));
bulletcount++;
}
}
and the background render code is as follows.
public class background extends GameObject{
private int width;
private Random random = new Random();
public background(int x, int y , ID id,Handler handler){
super(x,y,id,handler);
valy = 2;
width = getWidth(x,800);
}
public void tick() {
y += valy;
if(y>650)
handler.remove(this);
}
public void render(Graphics g) {
g.setColor(Color.white);
g.fillRoundRect(x,y,width,10,1,1);
}
public int getWidth(int x,int width1){
int wid;
while(true){
if((width - x) > 35) {
wid = random.nextInt((width-x-35));
break;
}
else{
x -= 10;
}
}
return wid;
}
}
the main class of game can see the order of flow of execution.
private void render() {
// render the Game
BufferStrategy bs = this.getBufferStrategy();
if(bs == null)
{
this.createBufferStrategy(3);
return;
}
Graphics g = bs.getDrawGraphics();
g.setColor(Color.black);
g.fillRect(0,0,WIDTH,HEIGHT);
handler.render(g);
hod.render(g); //handler is rendering every object in game
g.dispose();
bs.show();
}
private void tick() {
//update Game
handler.tick();
hod.tick();
spawner.tick(); // background is generating in spawner class.
}
I know that telling the problem with this much less code is hard for you all. But I have complete faith in this large and powerful community.
Thanks in advance. Don't hesitate to edit and point out my mistakes Thanks!
If I get the code right, the background is being generated after rendering the player which leads to the background being on top of the player. I would try calling spawner.tick() (where I guess the background is coming from given your code comment) before calling hod.tick(). Hopefully that should resolve this issue.
private void tick() { //update Game
spawner.tick();
handler.tick();
hod.tick();
}
I just had solution!
The problem was in handler.render(g); method:- which is a follows;
private void render(Graphics g){
for(int i =0; i<gameobj.size(); i++){//gameobj is linkedlist of GameObject
GameObject tempobj = gameobj.get(i);
tempobj.render(g);//getting every object and rendering them!
}
The problem was in loop the first object added in the game is player.So , it was getting render first after that the background was getting rendered.
It was a silly mistake.which got right by following change.->
private void render(Graphics g){
for(int i = gameobj.size()-1; i > 0; i--){//gameobj is linkedlist of GameObject
GameObject tempobj = gameobj.get(i);
tempobj.render(g);//getting every object and rendering them! from reverse
}
so, the last object renders first and the first object renders last.
Peace!
I'm trying to animate the sprite in my game when a button is pressed, but when I press the button, it skips the animation. Its supposed to go one pixel, change sprites, and then go one more pixel and change back. Here is the code
//for all
import java.nio.file.*;
import javax.imageio.ImageIO;
import java.io.IOException;
import java.awt.image.*;
import java.net.*;
import java.awt.*;
import javax.swing.*;
import static java.lang.invoke.MethodHandles.*;
import java.awt.event.*;
//my Mario class (cut down a lot)
class Mario {
// all numbers multiplied by 2 from OG game
protected Direction dir;
protected int x, y;
protected BufferedImage sprite;
protected String currentSpriteName;
public Mario() {
this.x = 54;
this.y = 808;
dir = Direction.RIGHT;
setSprite(MVCE.SMALLSTANDFACERIGHT);
currentSpriteName = MVCE.SMALLSTANDFACERIGHT;
}
public void moveRight(){
if(this.dir == Direction.LEFT){
this.dir = Direction.RIGHT;
}
else if(this.dir == Direction.RIGHT){
this.x+=1;
}
}
public void animateMoveRight(){
if (currentSpriteName.equals(MVCE.SMALLSTANDFACERIGHT)){
setSprite(MVCE.SMALLWALKFACERIGHT);
}
else if (currentSpriteName.equals(MVCE.SMALLWALKFACERIGHT)){
setSprite(MVCE.SMALLSTANDFACERIGHT);
}
}
public void jump() {
this.y -= 46;
}
public void setSprite(String spriteName) {
URL spriteAtLoc = MVCE.urlGenerator(spriteName);
this.sprite = MVCE.generateAndFilter(sprite, spriteAtLoc);
}
public void getSprite(){
System.out.println(this.currentSpriteName);
}
public void paint(Graphics g) {
Graphics2D g2 = (Graphics2D) g;
g2.drawImage(sprite, 0, 0, null); // DO NOT SET x and y TO ANYTHING,
// this sets 0,0 to top left!!
}
}
// my MarioRender class:
class MarioRender extends JLabel {
protected Mario marioSprite;
public MarioRender() {
marioSprite = new Mario();
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
marioSprite.paint(g2);
setBounds(marioSprite.x, marioSprite.y, marioSprite.sprite.getWidth(), marioSprite.sprite.getHeight());
}
public void moveMarioRight(){
marioSprite.moveRight();
marioSprite.animateMoveRight();
setLocation(this.marioSprite.getX(), this.marioSprite.getY());
repaint();
//this is my attempt to make it animate
marioSprite.moveRight();
marioSprite.animateMoveRight();
setLocation(this.marioSprite.getX(), this.marioSprite.getY());
repaint();
}
public void jumpMario() {
marioSprite.jump();
setLocation(this.marioSprite.x, this.marioSprite.y);
repaint();
}
}
// direction class, solely for moving
enum Direction {
LEFT, RIGHT
}
// my calling class, which I called MVCE where I make the frame
public class MVCE extends JFrame {
MarioRender m = new MarioRender();
JLabel bg;
public MVCE() {
bg = new JLabel();
this.setSize(868, 915);
this.setVisible(true);
this.add(bg, BorderLayout.CENTER);
bg.setLayout(null);
bg.add(m);
m.setBounds(m.marioSprite.x, m.marioSprite.y, m.marioSprite.sprite.getWidth(),
m.marioSprite.sprite.getHeight());
KeyListener kl = new MoveListener();
this.addKeyListener(kl);
this.setFocusable(true);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
public static final String SMALLSTANDFACERIGHT = "SmallStandFaceRight.bmp"; // 30
// x
// 32
public static final String SMALLJUMPFACERIGHT = "SmallJumpFaceRight.bmp"; // 32
// x
// 32
// generate URL
public static URL urlGenerator(String name) {
URL u = lookup().lookupClass().getResource(name);
return u;
}
// return image with filtered color
public static BufferedImage generateAndFilter(BufferedImage b, URL u) {
try {
b = ImageIO.read(u);
int width = b.getWidth();
int height = b.getHeight();
int[] pixels = new int[width * height];
b.getRGB(0, 0, width, height, pixels, 0, width);
for (int i = 0; i < pixels.length; i++) {
// System.out.println(pixels[i]);
if (pixels[i] == 0xFFff00fe) {
pixels[i] = 0x00ff00fe;
}
}
BufferedImage newSprite = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
newSprite.setRGB(0, 0, width, height, pixels, 0, width);
b = newSprite;
} catch (IOException e) {
System.out.println("sprite not found");
e.printStackTrace();
}
return b;
}
// key listener
class MoveListener implements KeyListener {
public void keyPressed(KeyEvent k) {
if ((k.getKeyCode() == 39)) {
m.moveMarioRight();
///THIS IS SUPPOSED TO MOVE HIM 1, change sprite, and automatically move him back, it moves 2 pixels but no animation
}
if (k.getKeyCode() == 83) { // S key
m.marioSprite.setSprite(SMALLJUMPFACERIGHT);
m.jumpMario();
}
}
public void keyReleased(KeyEvent k) {
}
public void keyTyped(KeyEvent k) {
}
}
public static void main(String[] args) {
MVCE m = new MVCE();
}
}
I tried putting this between the calls to marioMoveRight():
try {
Thread.sleep(200);
} catch(InterruptedException ex) {
Thread.currentThread().interrupt();
}
but it just delays the whole thing. I had also tried using an ActionListener, but I don't know how to make it react only when the key is pushed. as I had it,
I had this class inside of MVCE:
class TickListener implements ActionListener{
public void actionPerformed(ActionEvent a){
m.marioSprite.setSprite(Constants.SMALLWALKFACERIGHT);
repaint();
}
}
and this at the end of the MVCE constructor:
ActionListener ac = new TickListener();
final int DELAY = 1000;
Timer t = new Timer(DELAY, ac);
t.start();
but then, the Mario just moves automatically. I do not want to use a sprite sheet for this project, I am trying to do it as this guy did for SMB1.
Many problems, don't know which one or if any will fix the problem:
Don't use a KeyListener. If a component doesn't have focus the component won't receive the event. Instead use Key Bindings.
Don't use "==" to compare Objects. Instead you should be using the equals(...) method.
Don't override paintComponent. A painting method is for painting only. You should not be changing the bounds of the component in the painting method.
Do basic debugging (problem solving) before asking a question. A simple System.out.println(...) added to various methods will determine if the code is executing as you expect. Then when you ask a question you can ask a specific question telling us which block of code does not execute as you expect.
You never actually call the method animateMoveRight(), and if I understand correcly, that's what's changing the sprite. Also, I doubt that you see the sprite change when calling the same method twice in a row without any delay.
Try putting the animateMoveRight() method into the moveRight() or the moveMarioRight() method and, if neccessary because the animation is too fast, add your delay code back where you had it. Be careful not to let the main thread sleep, as this causes everything to freeze, so start another one or use a timer etc.
EDIT: Good timers
I'm not too familiar with the Timer class, so I end up using the Thread variant. There are many tutorials for that out there, just search for "java threads" or "java multithreading". This is IMO a solid tutorial you can check out.
I have some grids that is painted to screen one by one. I use arrow keys to move grids around as a group. Swing is said to be doubleBuffered by default so I believe frame.createBufferStrategy(2) is a bad practice but the problem is when I don't use manual double buffering, the grids are misaligned and some holes are appearing between them. Using manual double buffering fixes it.
I'm also experiencing some graphical problems(such as a dialog's buttons not displaying properly) in the actual program(not in SSCCE) so I thought it might be caused by the incorrect implementation of the double buffering.
Here is the SSCCE of the program, that causes grids to misalign when not manually double buffered:
package SSCCE;
import java.awt.Color;
import java.awt.FlowLayout;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.LayoutManager;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.image.BufferStrategy;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class Main {
boolean manuallyDoubleBuffered = false; //change this
static Main main;
public final JFrame frame = new JFrame();
public final Keys keys = new Keys();
private JPanel panel;
private BufferStrategy bufferStrategy;
public static void main(String[] args) {
main = new Main();
main.initiate();
// --START LOOP--
Thread loop = new Thread(main.new Looper());
loop.start();
}
public void initiate() {
frameInit();
keys.start();
}
private void frameInit() {
frame.setSize(1200, 750);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
setUpGUI();
if (manuallyDoubleBuffered)
frame.createBufferStrategy(2); // manual double buffering
bufferStrategy = frame.getBufferStrategy();
}
private void setUpGUI() {
panel = new JPanel() {
#Override
protected void paintComponent(Graphics g) {
Graphics2D g2d = (Graphics2D) g;
Main.main.rendering(g2d);
super.paintComponent(g);
}
};
LayoutManager layout = new FlowLayout();
frame.getContentPane().setBackground(Color.black);
panel.setLayout(layout);
panel.setOpaque(false);//
JButton but1 = new JButton("but1");
panel.add(but1);
frame.add(panel);
}
class Looper implements Runnable {
#Override
public void run() {
Main.main.gameLoop();
}
}
private void gameLoop() {
// variables are declared at start
while (true) {
if (manuallyDoubleBuffered)
paint(); // MANUAL double buffering
else
frame.repaint();// no manual double buffering
update();
try {
Thread.sleep(1000 / 60);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}// loop end
private void update() {
move();
}
private void rendering(Graphics2D g2d) {
// // testing
paintGrids(g2d);
}
private void move() {
x += sx;
y += sy;
}
int sx = 0; //speedX
int sy = 0; //speedY
//top left corner of the grid
int x = 0;
int y = 0;
private void paintGrids(Graphics2D g) {
for (int i = 0; i < 100; i++) {
for (int t = 0; t < 100; t++) {
g.setColor(Color.GRAY);
g.fillRect(i * 50 + x, t * 50 + y, 50, 50);
g.setColor(Color.BLACK);
g.drawString(i + "," + t, i * 50 + x, t * 50 + y + 10);
}
}
}
public void paint() {
// uses double buffering system.
do {
do {
Graphics2D g2d = (Graphics2D) bufferStrategy.getDrawGraphics();
g2d.fillRect(0, 0, frame.getWidth(), frame.getHeight());
try {
frame.paint(g2d);
} catch (NullPointerException e) {
e.printStackTrace();
}
g2d.dispose();
} while (bufferStrategy.contentsRestored());
bufferStrategy.show();
} while (bufferStrategy.contentsLost());
}
}
class Keys implements KeyListener {// Trimmed down to shorten SSCCE
private final int leftKey = 37; // left b.
private final int rightKey = 39; // Right b.
private final int upKey = 38;// up k.
private final int downKey = 40;// down k.
public void start() {
Main.main.frame.addKeyListener(this);
Main.main.frame.setFocusable(true);
}
private void left() {
Main.main.sx -= 10;
}
private void right() {
Main.main.sx += 10;
}
private void up() {
Main.main.sy -= 10;
}
private void down() {
Main.main.sy += 10;
}
#Override
public void keyPressed(KeyEvent e) {
// TODO Auto-generated method stub
System.out.println(e.getKeyCode());
switch (e.getKeyCode()) {
case leftKey:
left();
break;
case rightKey:
right();
break;
case downKey:
down();
break;
case upKey:
up();
break;
}
}
#Override
public void keyReleased(KeyEvent arg0) {
// TODO Auto-generated method stub
}
#Override
public void keyTyped(KeyEvent arg0) {
// TODO Auto-generated method stub
}
}// END OF THE KEYS CLASS
Oracle tutorials of swing does not explain the usage with a game loop. What is the best way to do it? Am I doing anything wrong?
In case the visual error is not reproduced on other computers, I'm uploading a screenshot:
Black lines are caused by the misalinging of the rectangles. They don't exist when manual double buffering is set to true.
Thanks in advance.
Edit: I've forgot to mention that the black lines occur when grids are moving.
I' have also found out, manual double buffering drastically reduces performance.
Edit 2 : I've fixed the problem and posted it as an answer but feel free to comment on my code. Main class(except the gameLoop) is similar to the actual main class I use in my program.
I couldn't see any change in the background. Here's the code change I made.
public static void main(String[] args) {
main = new Main();
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
main.initiate();
}
});
// --START LOOP--
Thread loop = new Thread(main.new Looper());
loop.start();
}
You must always start a Swing application with a call to SwingUtilities.invokeLater.
I've found the problem and writing here in case something like that ever happens to anyone else.
The problem was caused due to program being multi-threaded. Top left coordinates of the grids(x and y) were updated by the other thread in the middle of the paintGrids() method. Manual double buffering was slowing the program down (by hundreds of times) and that was allowing the paintGrids method to finish painting before x and y was updated by the keys.
To fix it I've added the following to the start of the paintGrids method:
int x = this.x;
int y = this.y;
i'm making a car game and i just got a problem with the size of the screen, it isnt allowing the car to move(it should be able to move on all the screen), maybe the size of the jpanel isnt the right size.
Main and JPanel :
public class MyCarGame extends JPanel implements Runnable {
CarPlayer Player;
public MyCarGame(GUI frame) {
Player = new CarPlayer(0, 0);
this.setSize(WidthFrame,HeightFrame);
this.addKeyListener(new KeyListener());
}
public void draw(Graphics g) {
Player.draw(g);
repaint();
}
/**
* #param args the command line arguments
*/
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g); //To change body of generated methods, choose Tools | Templates.
draw(g);
}
#Override
public void run() {
while (true) {
Player.move();
try {
Thread.sleep(10);
} catch (InterruptedException ex) {
Logger.getLogger(MyCarGame.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
private class KeyListener extends KeyAdapter{
#Override
public void keyPressed(KeyEvent e) {
switch (e.getKeyCode()) {
case KeyEvent.VK_RIGHT:
Player.xVel = 3;
break;
case KeyEvent.VK_LEFT:
Player.xVel = -3;
break;
case KeyEvent.VK_UP:
Player.yVel = -3;
break;
case KeyEvent.VK_DOWN:
Player.yVel = 3;
break;
}
}
#Override
public void keyReleased(KeyEvent e) {
switch (e.getKeyCode()) {
case KeyEvent.VK_RIGHT:
case KeyEvent.VK_LEFT:
Player.xVel = 0;
break;
case KeyEvent.VK_UP:
case KeyEvent.VK_DOWN:
Player.yVel = -1;
break;
}
}
}
public static int WidthFrame = 0;
public static int HeightFrame = 0;
public static void main(String[] args) {
// TODO code application logic here
new GUI().setVisible(true);
}
}
Player class :
public class CarPlayer {
int x;
int y;
Image img;
public int xVel;
public int yVel;
public CarPlayer(int x,int y){
img= new ImageIcon("images/PlayerCar.png").getImage();
this.x=x;
this.y=y;
xVel=0;
yVel=-1;
}
public int getX() {
return x;
}
public int getY() {
return y;
}
public void move(){
if(x+xVel<(MyCarGame.WidthFrame-50) && x+xVel>0)
x+=xVel;
if(y+yVel<(MyCarGame.HeightFrame-100) && y+yVel>0)
y+=yVel;
if(x<0){
xVel=0;
}
if(y<0){
yVel=0;
}
if(y>(MyCarGame.HeightFrame-100)){
yVel=0;
}
if(x>(MyCarGame.WidthFrame-50)){
xVel=0;
}
}
public void draw(Graphics g){
g.drawImage(img, x, y,50,100, null);
}
}
Frame class:
public class GUI extends javax.swing.JFrame {
/**
* Creates new form GUI
*/
MyCarGame g;
public GUI() {
this.setFocusable(false);
this.setResizable(false);
this.setExtendedState(JFrame.MAXIMIZED_BOTH);
MyCarGame.WidthFrame=this.getWidth();
MyCarGame.HeightFrame=this.getHeight();
g=new MyCarGame(this);
this.add(g);
g.setLocation(0, 0);
g.requestFocusInWindow();
Thread t= new Thread(g);
t.start();
}
}
You're relying on "magic" values, or attempting to...
public GUI() {
this.setFocusable(false);
this.setResizable(false);
this.setExtendedState(JFrame.MAXIMIZED_BOTH);
MyCarGame.WidthFrame = this.getWidth();
MyCarGame.HeightFrame = this.getHeight();
At this point, the frames size is still 0x0, because until you try and make it visible, the frame doesn't really know what screen it will be displayed on.
These values also do not take into account the possible insets that a frame border might have, and they change between platforms and look and feels (and can even change across different instances of the same platform)
Simple solution, get rid of it. static is not a good choice for this, a better solution would be to change your move method to require the caller to pass in the width and height of the available space...
For example...
public void move(int width, int height) {
Then when you need to call it, you could use something like...
Player.move(getWidth(), getHeight());
Avoid KeyListener, it's just troublesome, consider using the key bindings API instead. It provides better control over the level of focus required to trigger the key event...
Don't call repaint, directly or indirectly, from within any paint method, this is simply going to continue to raise a paint event, which will eventually consume your CPU
On my system, setResizable didn't play nicely with setExtendedState...you might want to consider getting rid of the setResizable option...
First of all, a little advice, for names of variables, use lower-case letters.
Second, don't ask to repaint on the draw() method on MyCarGame class, if you are already painting. You should ask it instead after you move the car. And your game loop need more control.
About the game loop, use this link: https://www.google.pt/#q=java+game+loop