I recently built my first simple side scrolling game in java, but I'm experiencing some lag and I don't know why. When I move my character sideways it seems to be changing speed, most of the time going fast but sometimes slowing down. It makes the feel of the game very odd. I hope someone can point me in the right direction here, I'll post my classes below:
Main Class:
public class MainGame {
public static void main(String[] args) {
Frame frame = new Frame();
}
}
Frame Class:
import javax.swing.JFrame;
public class Frame extends JFrame{
public Frame(){
add(new Board());
setTitle("2-D Test Game");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setSize(800,325);
this.setResizable(false);
setLocationRelativeTo(null);
setVisible(true);
}
}
Board Class:
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import javax.swing.*;
public class Board extends JPanel implements ActionListener{
private Player p;
private Timer timer;
private Image background;
public Board(){
super();
p = new Player();
addKeyListener(new ActionListener());
setFocusable(true);
ImageIcon i = new ImageIcon("C:/test.png");
background = i.getImage();
timer = new Timer(5,this);
timer.start();
}
public void actionPerformed(ActionEvent arg0) {
p.move();
repaint();
}
public void paint(Graphics g){
super.paint(g);
Graphics2D g2d = (Graphics2D) g;
g2d.drawImage(background,p.nx,0,null);
g2d.drawImage(p.getImage(), 350, p.getY(), null);
}
private class ActionListener extends KeyAdapter{
public void keyReleased(KeyEvent e){
p.keyReleased(e);
}
public void keyPressed(KeyEvent e){
p.keyPressed(e);
}
}
}
Player Class:
import java.awt.Image;
import java.awt.event.KeyEvent;
import javax.swing.*;
public class Player {
private Image img;
int x,y,dx,dy,nx;
long time;
private final int SPEED = 2;
private final int GRAV = 1;
boolean left,right,isJumping;
public Player(){
ImageIcon i = new ImageIcon("C:/plager.png");
img = i.getImage();
x = 350;
y = 160;
dx = 0;
dy = 0;
nx = 0;
left = false;
right = false;
isJumping = false;
}
public void move(){
x += dx;
nx = (nx-dx);
}
public int getX(){
return x;
}
public int getY(){
return y;
}
public Image getImage(){
return img;
}
public void keyReleased(KeyEvent e) {
int code = e.getKeyCode();
if(code == KeyEvent.VK_LEFT){
left = false;
if(right){
dx = SPEED;
}else{
dx = 0;
}
}else if(code == KeyEvent.VK_RIGHT){
right = false;
if(left){
dx = -SPEED;
}else{
dx = 0;
}
}
}
public void keyPressed(KeyEvent e) {
int code = e.getKeyCode();
if(code == KeyEvent.VK_LEFT){
left = true;
dx = -SPEED;
}else if(code == KeyEvent.VK_RIGHT){
right = true;
dx = SPEED;
}
}
}
Timer does not have a real time guarantee. So, you should calculate the time difference from the last time move was called and multiply that by the amount you are moving. It will make the numbers smaller so I would add another const to make it larger. It's actually better get the time delta in actionPerformed (or another process method) and pass it to all of the process or move methods so everything moves using the same time delta.
Related
Thanks in advance for help
I created a program that makes multiple bouncing balls When user clicks on the screen a new ball should appear and move around screen. But when i click on the screen a ball appears and doesn't moving at all. When another click happens, the ball created previously jumped to another position instantly.
this is the ball class: used to create balls
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.geom.Ellipse2D;
import java.util.Random;
import javax.swing.JComponent;
public class Ball extends JComponent implements Runnable{
private final int DIAMETER = 25;
private final int v1 = 5;
private final int v2 = -5;
private final Random rnd = new Random();
private int posX;
private int posY;
private Color color;
private int xVelocity;
private int yVelocity;
public Ball(int posX, int posY) {
this.posX = posX;
this.posY = posY;
this.color = randomColor();
this.xVelocity = rnd.nextBoolean()?v1:v2;
this.yVelocity = rnd.nextBoolean()?v1:v2;
}
public void move() {
if (posX < 15) {
xVelocity = -xVelocity;
} else if(posX > 475) {
xVelocity = -xVelocity;
}
if (posY < 0) {
yVelocity = -yVelocity;
} else if(posY > 475) {
yVelocity = -yVelocity;
}
posX +=xVelocity;
posY +=yVelocity;
}
public void draw(Graphics2D g2) {
g2.setColor(color);
g2.fill(new Ellipse2D.Double(posX,posY,DIAMETER,DIAMETER));
}
private static Color randomColor() {
int r = (int)(Math.random()*255);
int g = (int)(Math.random()*255);
int b = (int)(Math.random()*255);
Color rColor = new Color(r,g,b);
return rColor;
}
#Override
public void run() {
while(!Thread.interrupted()) {
move();
repaint();
try {
Thread.sleep(60);
} catch (InterruptedException ex) {}
}
}
}
this is the ballcomponent class: used to create the panel & display the balls
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class BallComponent extends JPanel{
private ArrayList<Ball> bList;
public BallComponent() {
bList = new ArrayList<Ball>();
this.setPreferredSize(new Dimension(500,500));
this.setBackground(Color.BLACK);
this.addMouseListener(new ClickListener());
}
public void paintComponent(Graphics g) {
Graphics2D g2 = (Graphics2D)g;
super.paintComponent(g2);
for (Ball a : bList) {
a.draw(g2);
}
}
private class ClickListener extends MouseAdapter{
public void mouseClicked(MouseEvent e) {
Ball a = new Ball(e.getX(),e.getY());
bList.add(a);
repaint();
Thread gameThread = new Thread(a);
gameThread.start();
}
}
public static void main(String[] args) {
JFrame frame = new JFrame();
frame.setTitle("Bouncing Balls");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new BallComponent());
frame.pack();
frame.setVisible(true);
}
}
Since you have a bunch of threads doing random stuff. You might as well have your JPanel update on it's own.
At the end of your main method.
new Thread( ()->{
while(true){
frame.repaint();
try{
Thread.sleep(60);
} catch(Exception e){
break;
}
}
}).start();
Usually you would do this with a timer task, but since you said you couldn't use a Timer, I just wrote a thread version.
I think repaint() is safe to call from off of the EDT since it just schedules a repaint. The paintComponent method and click method will only be called on the EDT. So you shouldn't get CCME. There are a bunch of race conditions with the multiple threads, but it seems like the only problem would be balls drawn out of position.
I'm trying to create a square that can move by pressing keys. When I Compiled & Ran the code it wouldn't move. So I began debugging (as well as I'm capable of). The problem seems to be that the run() function isn't being called. Why is this ? My understanding was that when using the interface Runnable, the run method is called automatically. I posted all the code in action.
Why isn't run() being called automatically and how can I change my program so it will call ?
Game.java
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class Game extends JPanel implements Runnable{
private static final int WIDTH = 800, HEIGHT = WIDTH / 12 * 9; //Widescreen
private Thread game_thread;
private boolean running = false;
public int x_speed = 0, y_speed = 0;
public Square square;
public Game(){
game_thread = new Thread("GameThread");
square = new Square(this);
addKeyListener(new KeyListener(){
#Override
public void keyPressed(KeyEvent e) {
if(e.getKeyCode() == e.VK_A){
x_speed = -1;
}
if(e.getKeyCode() == e.VK_D){
x_speed = 1;
}
if(e.getKeyCode() == e.VK_S){
y_speed = -1;
}
if(e.getKeyCode() == e.VK_W){
y_speed = 1;
}
}
#Override
public void keyReleased(KeyEvent e) {
}
#Override
public void keyTyped(KeyEvent e) {
}
});
}
public void start(){
System.out.println("Started");
game_thread.start();
running = true;
System.out.println(running);
}
public void stop(){
try{
running = false;
game_thread.join();
}catch(InterruptedException e){
e.printStackTrace();
}
}
public void paint(Graphics g){
super.paint(g);
Graphics2D g2d = (Graphics2D) g;
g2d.setColor(Color.BLACK);
g2d.fillRect(0, 0, WIDTH, HEIGHT);
square.render(g2d);
}
public void update(){
square.move();
System.out.println(x_speed + ", " + y_speed);
}
public void run(){
System.out.println("run method started");
while(running){
System.out.println("Running");
//Update screen info
update();
//Re-render
repaint();
try{
game_thread.sleep(10);
}catch(InterruptedException e){
e.printStackTrace();
}
}
}
public static void main(String args[]){
JFrame frame = new JFrame("Moving Thangs");
Game game = new Game();
frame.setSize(game.WIDTH, game.HEIGHT);
frame.setLocationRelativeTo(null);
frame.setDefaultCloseOperation(frame.EXIT_ON_CLOSE);
frame.setResizable(false);
frame.add(game);
frame.setVisible(true);
game.start();
}
}
Square.java
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
public class Square {
public static final int s_WIDTH = 80, s_HEIGHT = s_WIDTH;
public int x, y;
private Game game;
public Square(Game game){
x = 50;
y = 50;
this.game = game;
}
public void move(){
if(x >= 0 && x <= game.getWidth() - s_WIDTH){
x += game.x_speed;
}
if(y >= 0 && y <= game.getHeight() - s_HEIGHT){
y += game.y_speed;
}
}
public void render(Graphics2D g2d){
g2d.setColor(Color.ORANGE);
g2d.fillRect(x, y, s_WIDTH, s_HEIGHT);
}
}
When you create the thread using new Thread("GameThread") you don't pass this as a runnable to the thread. You need to pass it as the first argument in the constructor like new Thread(this, "GameThread") and then everything should work.
i'm teaching myself to program in java, and i have decided to make an space invader game. i got my ship moving in any direction, but i have a problem with my bullet being fired. now i know that my y-coordinate of the bullet are being updated every time my ship moves, but it is not firing. I need someone to help me get it to fire if possible. any help is welcome.
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import javax.swing.ImageIcon;
import javax.swing.JPanel;
import javax.swing.Timer;
public class Panel extends JPanel implements KeyListener, ActionListener{
private int x;
private int y;
private int dx;
private int dy;
int bx;
int by;
Rectangle bullet;
Timer timer;
private Image image;
public Panel() {
timer = new Timer(30, this);
setBackground(Color.black);
addKeyListener(this);
setFocusable(true);
timer.start();
x=130;
y=430;
bx=xPost()+55;
by=yPost();
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
ImageIcon ii= new ImageIcon("C:\\Users\\TriZam\\workspace\\LearningSprite\\ship.png");
image=ii.getImage();
g.drawImage(image, x, y, this);
doDrawing(g);
}
public void move(){
// thhis method will be placed inside the interferance ActionPerformed in order to move the ship and bullet
x += dx;
y += dy;
bx += dx;
by += dy;
}
public void keyPressed(KeyEvent e){
int key = e.getKeyCode();
if (key == KeyEvent.VK_LEFT) {
dx = -3;
if (x<=-25){
dx=0;
}
}
if (key == KeyEvent.VK_RIGHT) {
dx = 3;
if (x>=380 ){
dx=0;
}
}
if (key == KeyEvent.VK_UP) {
dy = -3;
if (y<=0 ){
dy=0;
}
}
if (key == KeyEvent.VK_DOWN) {
dy = 3;
if (y>=430 ){
dy=0;
}
}
if(key ==KeyEvent.VK_SPACE){
// bullet shooting and as you can see the y coordinate updates but bullet not moving.
bullet.y--;
System.out.println(bullet.y--);
}
}
public void keyReleased(KeyEvent e) {
int key = e.getKeyCode();
if (key == KeyEvent.VK_LEFT) {
dx = 0;
}
if (key == KeyEvent.VK_RIGHT) {
dx = 0;
}
if (key == KeyEvent.VK_UP) {
dy = 0;
}
if (key == KeyEvent.VK_DOWN) {
dy = 0;
}
}
#Override
public void keyTyped(KeyEvent e) {
// TODO Auto-generated method stub
}
#Override
public void actionPerformed(ActionEvent arg0) {
move();
repaint();
}
int yPost(){
return y;
}
int xPost(){
return x;
}
void doDrawing(Graphics g) {
bullet = new Rectangle(bx, by, 10, 10);
g.setColor(Color.white);
g.fillRect(bullet.x, bullet.y, bullet.width, bullet.height);
}
}
This is the main class
import javax.swing.JFrame;
public class MainClass extends JFrame {
private int FrameWidth;
private int FrameHeigh;
private Panel panel;
public MainClass(int width, int height ) {
panel= new Panel();
this.FrameWidth=width;
this.FrameHeigh=height;
setSize(FrameWidth,FrameHeigh);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
add(panel);
setVisible(true);
}
public static void main(String[] args) {
MainClass m= new MainClass(500, 600);
}
}
create the bullet once ; not inside paint and drawing - inside the constructor;
move this out of
void doDrawing(Graphics g) {
--> remove this from here and into the constructor --> bullet = new Rectangle(bx, by, 10, 10);
also when you update the location of the bullet dont use bx, by and separate variables; just do
bullet.x=....new location
bullet.y=....new location
Inside your key pressed space event, you should create an instance of a bullet. As of right now, you have a member variable Bullet; however it is null since it's never been initialized. That being said, you'll also want to move your bullet--this should be done in your move function.
I see that you have a bx, by, which I'm assuming is suppose to be the bullet's x and y position. However, the rectangle's coordinates never update. Instead of incrementing bx and by, update the rectangle (You'll also want to repaint the rectangle).
Hi I am new to making games, and I am working on one for a project. My code however does not make the Ship image move. Can someone help with a reasonable and simple explanation? Maybe even explain how to do this a bit simpler?
import java.awt.*;
import java.awt.event.*;
import java.util.ArrayList;
import javax.swing.*;
import java.awt.geom.*;
import java.util.Random;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
public class SpaceJam extends JPanel implements ActionListener
{
private Timer time;
private ImageIcon Ship = new ImageIcon("Spaceship.PNG");
private ImageIcon Back = new ImageIcon("background.PNG");
private ImageIcon Test = new ImageIcon("test.PNG");
private int x, y, dx, dy;
private Image CurrentImage = Ship.getImage();
public SpaceJam()
{
addKeyListener(new AL());
setFocusable(true);
x = 100;
y = 100;
dx = 0;
dy = 0;
time = new Timer(100, this);
time.start();
}
public void actionPerformed(ActionEvent e)
{
repaint();
move();
}
public void paint(Graphics g)
{
super.paint(g);
Graphics2D g2d = (Graphics2D) g;
g2d.drawImage(Back.getImage() ,0,0,null);
g2d.drawImage(CurrentImage, x, y, null);
}
public void move()
{
x += dx;
y += dy;
}
public class AL extends KeyAdapter
{
public void keyPressed(KeyEvent e){
int key = e.getKeyCode();
if(key == KeyEvent.VK_RIGHT)
{
dx = 5;
move();
}
if (key == KeyEvent.VK_LEFT)
{
dx = -5;
move();
}
}
}
}
Im having trouble getting the key pressed event to work, it doesn't recognize key input, can someone please help?
I have three classes player, exe, and board
Here is the board class
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyListener;
import java.awt.event.KeyEvent;
import javax.swing.JPanel;
import javax.swing.Timer;
import java.applet.*;
import java.awt.*;
import java.awt.event.*;
public class Board extends JPanel implements ActionListener{
private Timer timer;
private Player player;
//private Floor
public Board() {
addKeyListener( new TAdapter() );
setFocusable(true);
setBackground(Color.WHITE);
setDoubleBuffered(true);
player = new Player();
//floor = new Floor();
timer = new Timer(5, this);
timer.start();
}
public void paint(Graphics g) {
super.paint(g);
Graphics2D g2d = (Graphics2D)g;
g2d.drawImage(player.getImage(), player.getX(), player.getY(), this);
// g2d.drawImage(floor.getImage(), floor.getX(), floor.getY(), this);
//System.out.println(player.getX() + ", " + player.getY());
Toolkit.getDefaultToolkit().sync();
g.dispose();
}
public void actionPerformed(ActionEvent e) {
// checkPlayerOnGround();
player.move();
repaint();
}
private class TAdapter extends KeyAdapter implements KeyListener{
public void keyTyped(KeyEvent e) {
player.keyReleased(e);
System.out.println("Released");
}
public void keyPressed(KeyEvent e) {
player.keyPressed(e);
System.out.println("Pressed");
}
}
}
here is the player class
import java.awt.Image;
import java.awt.event.KeyEvent;
import javax.swing.ImageIcon;
import javax.swing.Timer;
import java.util.TimerTask;
public class Player{
private String player = "player.jpg";
private int moveX;
private int moveY;
private int x;
private int y;
private Image image;
private boolean canFall = false;
//private Timer jumpTimer = new Timer();
boolean canJump = true;
public Player() {
ImageIcon playeriImage = new ImageIcon(this.getClass().getResource(player));
image = playeriImage.getImage();
x = 40;
y = 60;
}
public void checkFall() {
if(canFall) {
moveY = -4;
}
}
public void move() {
//checkFall();
x = moveX + x;
y = moveY + y;
}
public int getX() {
return x;
}
public int getY() {
return y;
}
public Image getImage() {
return image;
}
public void keyPressed(KeyEvent e) {
int key = e.getKeyCode();
if (key == KeyEvent.VK_A) {
moveX = -4;
}
if (key == KeyEvent.VK_D) {
moveX = 4;
}
if (key == KeyEvent.VK_W) {
if(canJump)
this.jump();
}
}
public void keyReleased(KeyEvent e) {
int key = e.getKeyCode();
if (key == KeyEvent.VK_A) {
moveX = 0;
}
if (key == KeyEvent.VK_D) {
moveX = 0;
}
}
here is the exe class
import javax.swing.JFrame;
public class Exe extends JFrame {
public Exe() {
add(new Board());
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setSize(500, 500);
setLocationRelativeTo(null);
setTitle("TSA Video Game");
setResizable(false);
setVisible(true);
}
public static void main(String[] args) {
new Exe();
}
}
thanks in advance
KeyListener will only respond to key events IF the component is focusable AND has focus.
It would be better to use the Key Bindings API which will allow you to overcome this limitation (if you want to)
I would also recommend that you override paintComponent instead of paint. You should also not be calling Graphics#dispose, as you did not create the Graphics context, this could actually prevent anything from being painted on some systems
I would also suggest that a delay of 5 milliseconds (on your Timer) might be a little excessive (5 milliseconds = 200fps!).
A value of 16 (60fps) or 40 (25fps) may be more suitable and reduce the overhead on the system...IMHO