im making a side scroller and i dont know how to side scroll and im trying to scroll on the x and y axis
this is for a school project so i would like help as soon as possable
heres some of my code if you have question just ask. any advice is welcome.
source code http://www.mediafire.com/?fi1f9lv6qc2t5d7
gameCanvas.java
package Game;
import java.awt.*;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.image.BufferStrategy;
#SuppressWarnings("serial")
public abstract class GameCanvas extends Canvas implements Runnable,KeyListener{
public static final long ONE_SECOND_MILI = 1000;
protected int frameW;
protected int frameH;
protected long fps;
private long period = 15;
private BufferStrategy buff;
private Graphics graph;
private Color bckGround=(Color.GRAY);
private Image bckGround_img;
private Thread t;
boolean left;
boolean right;
boolean up;
boolean down;
int lastpressed;
int newlastpressed;
private String drawFps = "0";
public GameCanvas(int w,int h){
this.frameW=w;
this.frameH=h;
this.setIgnoreRepaint(true);
this.setBounds(0,0,frameW,frameH);
this.setBackground(Color.GREEN);
this.setVisible(true);
}
public GameCanvas(int w,int h,Color bck){
this.frameW=w;
this.frameH=h;
this.bckGround=bck;
this.setIgnoreRepaint(true);
this.setBounds(0,0,frameW,frameH);
this.setBackground(bckGround);
this.setVisible(true);
}
public void addNotify(){
super.addNotify();
this.createBufferStrategy(2);
this.buff=this.getBufferStrategy();
requestFocus();
startGame();
}
public void startGame(){
if (t==null){
t=new Thread(this);
t.start();
}
}
public void run(){
while(true){
long beginTime=System.currentTimeMillis();
// try {
// Thread.sleep(25);
// } catch (InterruptedException e1) {
// // TODO Auto-generated catch block
// e1.printStackTrace();
// }
Update();
Render();
Draw();
fps=System.currentTimeMillis() - beginTime ;
long sleepTime=period-fps;
if (sleepTime == 30) sleepTime = -1;
fps= ONE_SECOND_MILI / ((period * 2) - sleepTime);
try{
if (sleepTime > 0){
Thread.sleep(sleepTime);
}
}
catch(Exception e){
}
}
}
public void Render(){
graph = buff.getDrawGraphics();
if (!HasImgBackground()){
graph.setColor(bckGround);
graph.fillRect(0, 0, frameW, frameH);
}else{
graph.drawImage(bckGround_img, 0, 0, frameW, frameH,null);
}
graph.setColor(new Color(255,255,255));
graph.drawString("FPS: " + fps , 10, 15);
Paint(graph);
}
private void Draw(){
if(!buff.contentsLost()){
buff.show();
if(graph != null){
graph.dispose();
}
}
}
private boolean HasImgBackground(){
if (bckGround_img==null){
return false;
}
return true;
}
public void setBackgroundImg(Image image){
this.bckGround_img=image;
}
public void deleteBackground(){
this.bckGround_img=null;
}
public void keyPressed(KeyEvent e) {
if(e.getKeyCode()==KeyEvent.VK_LEFT){
left=true;
if(lastpressed!=4)
newlastpressed=lastpressed;
lastpressed=4;
}
if(e.getKeyCode()==KeyEvent.VK_UP){
up=true;
if(lastpressed!=1)
newlastpressed=lastpressed;
lastpressed=1;
}
if(e.getKeyCode()==KeyEvent.VK_RIGHT){
right=true;
if(lastpressed!=2)
newlastpressed=lastpressed;
lastpressed=2;
}
if(e.getKeyCode()==KeyEvent.VK_DOWN){
down=true;
if(lastpressed!=3)
newlastpressed=lastpressed;
lastpressed=3;
}
}
public void keyReleased(KeyEvent e) {
if(e.getKeyCode()==KeyEvent.VK_LEFT){
left=false;
if(up||right||down)
if(newlastpressed!=0)
lastpressed=newlastpressed;
}
if(e.getKeyCode()==KeyEvent.VK_UP){
up=false;
if(left||right||down)
if(newlastpressed!=0)
lastpressed=newlastpressed;
}
if(e.getKeyCode()==KeyEvent.VK_RIGHT){
right=false;
if(up||left||down)
if(newlastpressed!=0)
lastpressed=newlastpressed;
}
if(e.getKeyCode()==KeyEvent.VK_DOWN){
down=false;
if(up||right||left)
if(newlastpressed!=0)
lastpressed=newlastpressed;
}
}
public void keyTyped(KeyEvent e) {
}
abstract void Update();
abstract void Paint(Graphics g);
}
main.java
package Game;
import java.awt.Graphics;
import java.awt.Graphics2D;
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 java.util.ArrayList;
import javax.swing.JFrame;
public class Mainth extends GameCanvas{
private long timeDown = 0;
// private StopWatch t = new StopWatch();
static Collision coll=new Collision();
Character character=new Character();
static Image wallImage=ImageLoader.getImg().getImage("data/images/objects/brick_wall.png");
static Image bushImage=ImageLoader.getImg().getImage("data/images/objects/hedge_ver.png");
static ArrayList<Image> wallArray=new ArrayList<Image>();
static ArrayList<Image> wall2Array=new ArrayList<Image>();
static Sprite wallSprite;
static Sprite wall2Sprite;
static IndexCounter devIndex;
static Dude dev;
static Wall wall;
static Wall bush;
static Wall wall2;
/**not used*/
int x=0,y=0;
static ArrayList objects=new ArrayList<Entity>();
static ArrayList chars=new ArrayList<Dude>();
static ArrayList projectiles=new ArrayList<>();
public static void main(String[] args){
Mainth Canvas=new Mainth(1500,1000);
JFrame frame=new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setResizable(false);
frame.add(Canvas);
frame.pack();
frame.setSize(750, 400);
frame.setVisible(true);
Mainth.chars.add(dev);
Mainth.objects.add(wall);
Mainth.objects.add(bush);
Mainth.objects.add(wall2);
}
public Mainth(int w,int h){
super(w,h);
this.addKeyListener(this);
CharacterCreation();
}
public void CharacterCreation(){
wallArray.add(wallImage);
wall2Array.add(bushImage);
wallSprite=new Sprite(wallArray,1);
wall2Sprite=new Sprite(wall2Array,1);
dev=new Dude(character.LinkStandingDown,character.LinkStandingDown.getID(),100,300);
devIndex=new IndexCounter(character.LinkWalkingDownArray.size(),3);
wall=new Wall(wallSprite,1,100,50){};
bush=new Wall(wall2Sprite,1,185,55){};
wall2=new Wall(wallSprite,1,100,225){};
bush.setHardness(1);
}
Movement movem=new Movement();
void Update() {
movem.movement(dev, character, left, right, up, down, lastpressed, devIndex);
dev.move();
coll.objectCollision(chars,objects);
devIndex.Counter();
}
void Paint(Graphics g) {
bush.Draw(g,0);
wall.Draw(g,0);
wall2.Draw(g,0);
dev.Draw(g,devIndex.getIndex());
Graphics2D g2d= (Graphics2D) g;
}
public void animation(){
String[] animationSprites=dev.getImgs("walk_down");
int aniTime=0;
aniTime++;
}
public ArrayList<Entity> exportObjects(){
return objects;
}
public ArrayList<Dude> getMainChar(){
return chars;
}
}
What exactly isn't working in the game?
Basic side scrolling logic consists of moving the background, the characters, and the obstacles a certain increment as movement keys are held down. If the screen is "moving right," we are actually moving all of these elements to the left. If the screen is "moving left," then we are doing the opposite. Also, if an entity is moving and has a certain target point, make sure and update the coordinates of this point as you scroll, or the entity will keep moving until it reaches its original on-screen destination. You should also implement code that stops the screen from scrolling too far (thus losing the game).
There are many ways to use side-scrolling, such as scrolling when a character moves a certain distance from the center of the screen, or having controls that move the screen independently from a character (such as in an rts when one scrolls around the map). An easier way might be to always have the player be in the center of the screen, and just have the background and other entities scroll back and forth around him.
You need to coordinate systems:
world coordinates - are always constant
(local) frame coordinate - where objects should be shown in your window now
every object has only world coordinates. And you have a window in this big world. This window has its coordinates in world coordinates. To scroll the view in your window you just need to change it's world coordinates.
Of course you need a code, that renders all objects in you window for any correct position of your movable window. It can be like:
void renderFrame(Rectangle frame) {
for(GameObject go : gameObjects) {
if(frame.contains(go.getGlobalCoordinates())) {
Rectangle windowCoordinates = new Rectangle();
windowCoordinates.x = go.getGlobalCoordinates().x - frame.x;
windowCoordinates.x = go.getGlobalCoordinates().y - frame.y;
windowCoordinates.x = go.getGlobalCoordinates().width - frame.width;
windowCoordinates.x = go.getGlobalCoordinates().height - frame.height;
go.paint(g2, windowCoordinates);
}
}
}
Related
So I am creating a brick breaker game. I got the paddle moving using key listeners, however I got some advice to use key bindings instead. I've been reading and looking everywhere on this and I think I've kinda got the idea but the implementation is confusing me a bit.
For my paddle, I created it with the paint component. Hence this is what I want to be able to move left and right with the arrow keys. However, all the key bindings explanations I find seem to use JComponent which the paddle is not. Is there a way around it or will I have to make my paddle a JComponent image icon instead? If so would I just load the image into my Paddle class?
Also what's the best way of structuring my code to contain the key bindings? For instance am I better off creating a whole new class for it, or putting it in e.g. my GamePanel class
Any tips?
Here is some of my code so you can get an idea:
Main Class:
public class BrickBreakerGameApp {
public static void main(String[] args) {
int pw = 500;
int ph = 900;
GamePanel gPanel = new GamePanel(pw,ph);
GameFrame gFrame = new GameFrame();
gFrame.getContentPane().add(gPanel); //add game panel to frame
// gFrame.addKeyListener(gPanel); //adds the key listener for the game panel frame
gFrame.pack();
gFrame.setVisible(true); //make frame visible
}
}
GameFrame class:
import java.awt.Dimension;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
public class GameFrame extends JFrame {
//Global Variables
public int frameWidth = 500;
public int frameHeight = 800;
//create constructor
GameFrame () {
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); //so app closes properly
this.setPreferredSize(new Dimension(frameWidth, frameHeight)); //set frame size
this.setTitle("Brick Breaker Game");
ImageIcon icon = new ImageIcon("res/brick-breaker-logo.jpg"); //create image icon
this.setIconImage(icon.getImage()); //update frame icon
this.pack();
}
}
GamePanel Class
import java.awt.Color;
import java.awt.event.*;
import javax.swing.InputMap;
import javax.swing.JComponent;
import javax.swing.JPanel;
import javax.swing.KeyStroke;
import java.awt.*;
/**
* This class handles the game panel as a whole
*/
public class GamePanel extends JPanel {
// Global Variables
private Ball ball;
private Paddle paddle;
private Bricks bricks;
private GameFrame gameFrame;
private int width;
private int height;
private int[] bArray = new int[20];
// create a constructor
GamePanel (int gamePanelWidth, int gamePanelHeight) {
this.setWidth(gamePanelWidth);
this.setHeight(gamePanelHeight);
initialiseGame();
this.isVisible();
}
private void initialiseGame() {
ball = new Ball(10, 520, 30, 30); //create the ball object
paddle = new Paddle(this, 50, 700, 100, 10); //creates paddle object
bricks = new Bricks(10, 10, 60, 30, bArray); //creates the bricks object
//for key binding initialisation
MotionAction motion = new MotionAction(paddle, 24);
}
// paint all the elements in
public void paintComponent(Graphics g) {
super.paintComponent(g);
// background
g.setColor(Color.black);
g.fillRect(0, 0, getWidth(), getHeight());
// paddle
// g.setColor(Color.CYAN);
// g.fillRect(paddle.getPaddleX(), paddle.getPaddleY(), paddle.getPaddleWidth(), paddle.getPaddleHeight());
// ball
g.setColor(Color.MAGENTA);
g.fillOval(ball.getBallX(), ball.getBallY(), ball.getBallWidth(), ball.getBallHeight());
// brick
g.setColor(Color.GREEN);
g.fillRect(bricks.getBrickX(), bricks.getBrickY(), bricks.getBrickWidth(), bricks.getBrickHeight());
}
//add any unimplemented methods because we are using an interface
/* #Override
public void actionPerformed(ActionEvent e) {
}
#Override
public void keyPressed(KeyEvent e) {
if(e.getKeyCode() == KeyEvent.VK_LEFT) {
paddle.moveLeft();
}
if(e.getKeyCode() == KeyEvent.VK_RIGHT) {
paddle.moveRight();
}
this.repaint();
}
#Override
public void keyTyped(KeyEvent e) {
}
#Override
public void keyReleased(KeyEvent e) {
} */
//create get and set methods for all panel attributes
public int getWidth() {
return width;
}
public void setWidth(int pWidth) {
this.width = pWidth;
}
public int getHeight() {
return height;
}
public void setHeight(int pHeight) {
this.height = pHeight;
}
}
Paddle class:
import javax.swing.AbstractAction;
import java.awt.event.*;
public class Paddle extends AbstractAction implements ActionListener {
//Global Variables
private int paddleWidth;
private int paddleHeight;
private int paddleX; //paddle x position
private int paddleY;
private GamePanel gamePanel;
//create paddle constructor
Paddle() {
}
//create a paddle constructor based off parameters
Paddle(GamePanel gPanel, int pX, int pY, int pWidth, int pHeight) {
//set the values
this.setPaddleWidth(pWidth);
this.setPaddleHeight(pHeight);
this.setPaddleX(pX);
this.setPaddleY(pY);
this.setGamePanel(gPanel);
}
//create get and set methods for all paddle attributes
public int getPaddleWidth() {
return paddleWidth;
}
public void setPaddleWidth(int pWidth) {
this.paddleWidth = pWidth;
}
public int getPaddleHeight() {
return paddleHeight;
}
public void setPaddleHeight(int pHeight) {
this.paddleHeight = pHeight;
}
public int getPaddleX() {
return paddleX;
}
public void setPaddleX(int pX) {
this.paddleX = pX;
}
public int getPaddleY() {
return paddleY;
}
public void setPaddleY(int pY) {
this.paddleY = pY;
}
public GamePanel getGamePanel() {
return gamePanel;
}
public void setGamePanel(GamePanel gPanel) {
this.gamePanel = gPanel;
}
//move the paddle left if it is not already positoned at 0 (far left)
public void moveLeft() {
try {
if(getPaddleX() <= 0) {
setPaddleX(0);
System.out.println("less than 0, x: " + getPaddleX());
}
else if (getPaddleX() > 0) {
setPaddleX(getPaddleX()-10); //to move paddle left -10
// gamePanel.repaint(getPaddleX()+10, getPaddleY(), getPaddleWidth(), getPaddleHeight()); //repaint old position
// gamePanel.repaint(getPaddleX(), getPaddleY(), getPaddleWidth(), getPaddleHeight()); //repaint new position
System.out.println("left, x: " + getPaddleX());
}
}
catch (Exception e) {
}
}
//move the paddle right if it is not already positioned to the far right of the panel
public void moveRight() {
if(getPaddleX() >= gamePanel.getWidth() - getPaddleWidth()) { //dont move the paddle if it is on the right edge of the panel
setPaddleX(gamePanel.getWidth() - getPaddleWidth());
System.out.println("right1, x:" + getPaddleX());
}
else if ((getPaddleX()+getPaddleWidth()) <= gamePanel.getWidth()){ //if the paddle is within the panel bounds
setPaddleX(getPaddleX() + 10); //to move paddle right +10
System.out.println("right, x:" + getPaddleX());
}
}
}
Here are some of the main resources I've being trying:
This one I tried but got really confused on the best way to integrate it into my own code --> https://tips4java.wordpress.com/2013/06/09/motion-using-the-keyboard/
This one I tried until I realised I don't have jcomponent --> https://coderanch.com/t/606742/java/key-bindings
You could register the key bindings with the GamePanel, or the frame content pane (both extend from JComponent). E.g.:
public class DemoFrame extends JFrame {
public static void main(String[] args) throws Exception {
new DemoFrame().setVisible(true);
}
public DemoFrame() {
setSize(800, 600);
setLocationRelativeTo(null);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
{
String actionId = "left";
KeyStroke keyStroke = KeyStroke.getKeyStroke("LEFT");
getRootPane().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(keyStroke, actionId);
getRootPane().getActionMap().put(actionId, new AbstractAction() {
#Override
public void actionPerformed(ActionEvent e) {
actionLeft();
}
});
}
}
private void actionLeft() {
System.out.println("left");
}
}
This is an UI that makes a ball go down in a diagonal way, but the ball stays static; it seems something is not working adecuatedly with the threads. Could you please, tell me how to make the ball move?
Please download a ball and change the directory so the program can find where your ball is allocated. It's not necessary to download the soccer pitch but if you want, it's OK. Finally, I have to thank you for spending time in search of this malfunctioning.
import java.awt.BorderLayout;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JButton;
import java.awt.event.ActionListener;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.RenderingHints;
import java.awt.event.ActionEvent;
import java.awt.geom.Rectangle2D;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import javax.imageio.ImageIO;
import java.io.File;
class Animation extends JFrame implements ActionListener { //Frame and listener
Rectangle2D dimensions = new Rectangle2D.Double(0,0,850,595); //Not implemented limits
JButton animate, stop;
Runnable runnable;
Thread move;
public Animation() {
setLayout(new BorderLayout()); //BorderLayout disposition
setTitle("Pelota en acción");
animate = new JButton("Animate it!"); //Button to create balls
animate.setBounds(0,0,120,30);
animate.addActionListener(new ActionListener(){
#Override
public void actionPerformed(ActionEvent e) {
Image ball = null;
new Layout().createEllipse(ball);
runnable = new Layout();
move = new Thread(runnable);
move.start();
}
});
stop = new JButton("Freeze"); //Button to interrupt thread (not implemented)
stop.setBounds(0,0,120,30);
stop.addActionListener(new ActionListener(){
#Override
public void actionPerformed(ActionEvent e) {
move.interrupt();
Layout.running = false;
}
});
JPanel subPanel = new JPanel(); //Layout with its buttons situated to the south
subPanel.add(animate);
subPanel.add(stop);
add(subPanel,BorderLayout.SOUTH);
add(new Layout());
}
public static void main(String[] args) {
Animation ventana = new Animation();
ventana.setSize(850,625);
ventana.setLocationRelativeTo(null);
ventana.setVisible(true);
ventana.setDefaultCloseOperation(EXIT_ON_CLOSE);
}
#Override
public void actionPerformed(ActionEvent e) {} //Tag
} //Class close
class Layout extends JPanel implements Runnable { //Layout and thread
int X,Y; //Coordenadas
static boolean running = true; //"To interrupt the thread" momentaneously.
static ArrayList<Image> balls = new ArrayList<>(); //Balls collection
#Override
public void run () { //Just moves ball towards Narnia xd
while(running) {
X++; Y++;
System.out.println(X+" "+Y);
repaint();
updateUI();
try {
Thread.sleep(4);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D)g;
g2.addRenderingHints(new RenderingHints(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON));
repaint();
updateUI();
try {
URL url = new URL("https://www.freejpg.com.ar/image-900/9c/9ca2/F100004898-textura_pasto_verde_linea_de_cal.jpg");
Image picture = ImageIO.read(url);
g.drawImage(picture,0,0,null);
} catch(IOException e){
System.out.println("URL image was not found");
}
finally {
try {
//----------------------------------------------------------------------------
Image picture = ImageIO.read(new File("C:\\Users\\Home\\Desktop\\Cancha.jpg")); //Pitch
//----------------------------------------------------------------------------
g.drawImage(picture, 0, 0, null);
} catch (IOException ex) {
System.out.println("Pitch image was not found");
}
}
for (Image ball : balls) { //I add balls to the Layout
g2.drawImage(ball,X,Y,100,100,null);
}
}
public void createEllipse (Image ball) { //Method that adds balls to the collection
try {
//-------------------------------------------------------------------- Ball
ball = ImageIO.read(new File("C:\\Users\\Home\\Desktop\\Pelota.png")); //Change this
//-------------------------------------------------------------------- Ball
} catch(IOException ex) {
System.out.println("Any balls were found");
}
balls.add(ball);
}
}
So to break your code down:
When the button is pressed, you execute the following code:
Image ball = null;
new Layout().createEllipse(ball);
runnable = new Layout();
move = new Thread(runnable);
move.start();
This will create a new layout. The run() method of this will increase the X and Y variables. They are declared here:
int X,Y; //Coordenadas
Those are instance variables, this means they belong to your newly created Layout.
Then you call repaint() on the new Layout, which will do nothing, because this new Layout has not been added to some window.
So, how do you fix this?
First, you have to keep the original Layout around:
class Animation extends JFrame { // no need to implement ActionListener
Rectangle2D dimensions = new Rectangle2D.Double(0,0,850,595); //Not implemented limits
JButton animate, stop;
Thread move;
Layout layout;
Then remember the Layout when you create it:
// before: add(new Layout());
layout = new Layout();
add(layout);
Then use the layout in your ActionListener:
layout.createEllipse(ball);
move = new Thread(layout);
move.start();
This might have some problems with concurrency (Swing is not thread-safe), so for good measure, you should call repaint() in the AWTEventThread:
// in run(), was repaint():
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
repaint();
}
});
Now, there are some cleanup tasks left:
Delete this code:
#Override
public void actionPerformed(ActionEvent e) {} //Tag
It's no longer needed, because you don't implement ActionListener.
Drop the static modifiers from some fields, and add volatile:
volatile int X,Y; //Coordenadas
volatile boolean running = true; //"To interrupt the thread" momentaneously.
ArrayList<Image> balls = new ArrayList<>(); //Balls collection
volatile is needed for variables that are accessed from more than one thread.
Also remove repaint() and resetUI() from the paint method. You don't need them.
For the pictures in paint: you should cache them. Store them in a field, so you don't have to load the picture every time.
When all this is done, your code is much cleaner, but there are still some warts that should be addressed. But at least you have something working.
Johannes has already spoken about many of the things which are wrong with your original example, so I won't go over many of them again.
This example makes use of a Swing Timer instead of a Thread as the main "animation" loop. It also focuses on demonstrating encapsulation and responsibility.
For example, the AnimtionPane is responsible for managing the balls, managing the animation loop and paint. It isn't, however, responsible for determining "how" the balls are updated or paint, it only provides the timing and functionality to make those things happen.
A couple of the glaring issues I can see are:
Trying to load resources from within the paintComponent method. This is a bad ideas, as it could slow you paint pass down, causing your UI to lag
Calling repaint and updateUI from within the paintComponent method. You should avoid causing any new updates to the UI from occurring during a paint process. This could cause your program to run wide and consume all the CPU cycles, not only making your app non-responsive, but also the whole system.
Some very quick points
Swing is not thread safe. You should never update the UI (or anything the UI relies on) from outside the context of the Event Dispatching Thread. This example uses a Swing Timer as it allows the delay to occur of the EDT (and not block the UI), but it's updates are triggered within the EDT, allowing us to safely update the UI from within
You create multiple instances of Layout, meaning that the one on the screen isn't the one which is been updated
Your "freeze" logic is broken. It will never "freeze" anything
Runnable example
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.Shape;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.geom.Ellipse2D;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Random;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frame = new JFrame();
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
private AnimationPane animationPane;
public TestPane() {
setLayout(new BorderLayout());
animationPane = new AnimationPane();
JButton actionButton = new JButton("Start");
actionButton.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent evt) {
if (animationPane.isAnimating()) {
animationPane.stop();
actionButton.setText("Start");
} else {
animationPane.start();
actionButton.setText("Stop");
}
}
});
add(animationPane);
add(actionButton, BorderLayout.SOUTH);
}
}
// This is just makes it seem more random ;)
private static Random RANDOM = new Random();
public class Ball {
private int x;
private int y;
private int xDelta;
private int yDelta;
private Color color;
private Shape shape;
public Ball(Color color) {
shape = new Ellipse2D.Double(0, 0, 10, 10);
this.color = color;
// Get some random motion
do {
xDelta = RANDOM.nextInt(6) + 2;
yDelta = RANDOM.nextInt(6) + 2;
} while (xDelta == yDelta);
}
public void update(Rectangle bounds) {
x += xDelta;
y += yDelta;
if (x + 10 > bounds.x + bounds.width) {
x = bounds.x + bounds.width - 10;
xDelta *= -1;
} else if (x < bounds.x) {
x = bounds.x;
xDelta *= -1;
}
if (y + 10 > bounds.y + bounds.height) {
y = bounds.y + bounds.height - 10;
yDelta *= -1;
} else if (y < bounds.y) {
y = bounds.y;
yDelta *= -1;
}
}
public void paint(Graphics2D g2d) {
// This makes it easier to restore the graphics context
// back to it's original state
Graphics2D copy = (Graphics2D) g2d.create();
copy.setColor(color);
copy.translate(x, y);
copy.fill(shape);
// Don't need the copy any more, get rid of it
copy.dispose();
}
}
public class AnimationPane extends JPanel {
// This does not need to be static
private List<Ball> balls = new ArrayList<>(); //Balls collection
private Timer timer;
private List<Color> colors;
public AnimationPane() {
colors = new ArrayList<>(8);
colors.add(Color.RED);
colors.add(Color.GREEN);
colors.add(Color.BLUE);
colors.add(Color.CYAN);
colors.add(Color.MAGENTA);
colors.add(Color.ORANGE);
colors.add(Color.PINK);
colors.add(Color.YELLOW);
timer = new Timer(40, new ActionListener() {
#Override
public void actionPerformed(ActionEvent evt) {
if (RANDOM.nextBoolean()) {
makeBall();
}
Rectangle bounds = new Rectangle(new Point(0, 0), getSize());
for (Ball ball : balls) {
ball.update(bounds);
}
repaint();
}
});
makeBall();
}
protected void makeBall() {
Collections.shuffle(colors);
balls.add(new Ball(colors.get(0)));
}
public boolean isAnimating() {
return timer.isRunning();
}
public void start() {
timer.start();
}
public void stop() {
timer.stop();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g.create();
g2.addRenderingHints(new RenderingHints(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON));
// Bad ideas. Repaint will cause a new paint event to be posted, causing your
// UI to run away - consuming all your CPU cycles in a singulator forms
// and destorys the known universe
//repaint();
// This doesn't do what you think it does and there shouldn't be
// reason for you to call it
//updateUI();
// This is a bad idea as it could cause the paint cycles to slow down
// destorying the responsiveness of your app
// Besids, you should be passing this as the ImageObserver
// try {
// URL url = new URL("https://www.freejpg.com.ar/image-900/9c/9ca2/F100004898-textura_pasto_verde_linea_de_cal.jpg");
// Image picture = ImageIO.read(url);
// g.drawImage(picture, 0, 0, null);
// } catch (IOException e) {
// System.out.println("URL image was not found");
// } finally {
// try {
// //----------------------------------------------------------------------------
// Image picture = ImageIO.read(new File("C:\\Users\\Home\\Desktop\\Cancha.jpg")); //Pitch
// //----------------------------------------------------------------------------
// g.drawImage(picture, 0, 0, null);
// } catch (IOException ex) {
// System.out.println("Pitch image was not found");
// }
// }
// This is "bad" per say, but each ball should have it's own
// concept of location
// for (Image ball : balls) { //I add balls to the Layout
// g2.drawImage(ball, X, Y, 100, 100, null);
// }
for (Ball ball : balls) {
ball.paint(g2);
}
// I made a copy of the graphics context, as this is shared
// with all the other components been painted, changing the
// render hints could cause issues
g2.dispose();
}
}
}
INTRODUCTION
Hi guys, I was learning how to make a little game where you are a racquet (rectangle) and all the asteroids (enemies) are falling down the screen and you have to avoid them
PROBLEM
When the game starts the enemies (asteroids) are paint on the screen in a very high speed, and they occupy all the size of the screen so there is no way to avoid them (try the game), I just want that between painting an enemy and the next one there is a mininum time to delay (for example 0,5 seconds).
But I just don't know how to do that, I tried to use Thread.sleep() and TimeUnit but they just make the game slower.
Surfing on stackoverflow I've find out that I may try to use Swing timers, I've read some stuff on the web but I want to know how can I use swing timers in my code (if they can solve my problem).
Here it is the code:
The main class:
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.util.ArrayList;
import java.util.Random;
import java.util.concurrent.TimeUnit;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.Timer;
#SuppressWarnings("serial")
public class Game extends JPanel {
Racquet racquet = new Racquet(this);
Enemy Enemy = new Enemy(this);
static ArrayList<Enemy> enemyList = new ArrayList<Enemy>();
public Game() {
addKeyListener(new KeyListener() {
#Override
public void keyTyped(KeyEvent e) {
}
#Override
public void keyReleased(KeyEvent e) {
racquet.keyReleased(e);
}
#Override
public void keyPressed(KeyEvent e) {
racquet.keyPressed(e);
}
});
setFocusable(true);
}
/** TO SET THE RANDOM POSITION ON WHERE THE ENEMIES HAVE TO APPEAR ON THE SCREEN **/
public int random(int x, int y, ArrayList<Enemy> pa){
int r = 0;
for(int i = 0; i<pa.size(); i++){
Random rand = new Random();
r = rand.nextInt(x+y)-1;
return r;
}
return r;
}
/** letting the enemies move on the screen **/
private void move() {
for(int i = 0; i < enemyList.size(); i++){
enemyList.get(i).move();
}
racquet.move();
}
/** Painting on the screen enemies and the racquet **/
#Override
public void paint(Graphics g) {
super.paint(g);
Graphics2D g2d = (Graphics2D) g;
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
for(int i = 0; i < enemyList.size(); i++){
enemyList.get(i).paint(g2d);
}
racquet.paint(g2d);
}
public void gameOver() {
JOptionPane.showMessageDialog(this, "Game Over", "Game Over", JOptionPane.YES_NO_OPTION);
System.exit(ABORT);
}
public void createEnemy(){
enemyList.add(new Enemy(this));
}
public static void main(String[] args) throws InterruptedException {
JFrame frame = new JFrame("Asteroids");
Game game = new Game();
frame.add(game);
frame.setSize(300, 400);
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
game.random(200, 300, enemyList);
while (true) {
game.createEnemy();
game.move();
game.repaint();
Thread.sleep(5);
}
}
}
The racquet class:
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.event.KeyEvent;
public class Racquet {
private static final int Y = 330;
private static final int WIDTH = 30;
private static final int HEIGHT = 6 ;
int x = 0;
int xa = 0;
private Game game;
public Racquet(Game game) {
this.game = game;
}
/** letting the racquet moves on the screen **/
public void move() {
if (x + xa > 0 && x + xa < game.getWidth() - WIDTH)
x = x + xa;
}
/** Creating the rectangle racquet **/
public void paint(Graphics2D g) {
g.fillRect(x, Y, WIDTH, HEIGHT);
}
/** // Setting xa everytime to 0, if we don't do this it just takes a single pression to go to a direction until we press the other key **/
public void keyReleased(KeyEvent e) {
xa = 0;
}
/** Choosing the direction **/
public void keyPressed(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_LEFT)
xa = -1;
if (e.getKeyCode() == KeyEvent.VK_RIGHT)
xa = 1;
}
public Rectangle getBounds() {
return new Rectangle(x, Y, WIDTH, HEIGHT);
}
public int getTopY() {
return Y;
}
}
And the enemy class:
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.util.*;
public class Enemy {
private Game game;
int x = 0;
int y = 0;
int xa = 1;
int ya = 1;
/** Generating a random position where the enemies have to appear **/
public Enemy(Game game){
this.game = game;
x = game.random(0, 320, game.enemyList);
}
/** Paint the enemies **/
public void paint(Graphics2D g) {
g.fillRect(x, y, 20, 20);
}
/** move the enemies and detect collisions **/
public void move(){
y += ya;
if(collision()){
game.gameOver();
}
}
/** returns true if the enemy rectangle touch the racquet **/
public boolean collision(){
return game.racquet.getBounds().intersects(getBounds());
}
public Rectangle getBounds() {
return new Rectangle(x, y, 20, 20);
}
}
Suggestions:
Your game "tick" time is 5 mSecs, a not quite reasonable time. I suggest that
You get rid of the "magic" number, using a constant instead,
and make the tic a little bigger, say 12 to 15 mSec.
Also better to use a Swing Timer or to at least make sure that you do your while loop in a defined background thread.
Most importantly, you're creating a new enemy with each tick of your game loop, and that's too fast. Instead:
Don't create a new Enemy with each tick,
Instead save the time that the last enemy was created in a field,
Check the delta-time, the current system time - lastEnemyCreationTime, inside of your game-loop,
Only create a new enemy if the delta-time is greater than a reasonable value, either a constant value or a field (not a magic number).
On creation of the new enemy, reset the lastEnemyCreationTime to the current system time.
Unrelated recs:
Override the JPanel's paintComponent, not the paint method as this will give you double buffering by default which can lead to smoother perceived graphics.
Favor using Key Bindings over a KeyListener as this will help to easily eliminate focus issues inherent with use of KeyListeners.
I've started creating a simple java game and at the moment I have the Game Window created with a screen and a basic Player class. The player image however won't draw onto the screen despite the program not giving me any errors so I'm not sure where to start debugging for the problem maybe someone could help me out?
Here are the classes:
import java.awt.*;
import javax.swing.*;
#SuppressWarnings("serial")
public class GameWindow extends JFrame {
public static int windowWidth = 600;
public static int windowHeight = 600;
public static void main(String[] args) {
new GameWindow();
}
public GameWindow() {
this.setSize(windowWidth, windowHeight);
this.setTitle("Berzerk Clone");
this.setVisible(true);
// Defaults the window to be set in the middle of the screen
this.setLocationRelativeTo(null);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
GameDrawing drawGame = new GameDrawing();
this.add(drawGame, BorderLayout.CENTER);
}
}
Drawing Class:
import java.awt.*;
import javax.swing.*;
#SuppressWarnings("serial")
public class GameDrawing extends JComponent {
PlayerHuman p;
public GameDrawing() {
p = new PlayerHuman(300, 300);
}
public void paint(Graphics g) {
super.paint(g);
Graphics2D graphics = (Graphics2D)g;
graphics.setColor(Color.BLACK);
graphics.fillRect(0, 0, GameWindow.windowWidth,GameWindow.windowHeight);
graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
graphics.drawImage(p.getPlayerImage(), 300, 300, null);
}
}
Player class:
import java.awt.*;
import java.awt.image.*;
import java.io.*;
import javax.imageio.*;
public class PlayerHuman extends GlobalPosition {
BufferedImage basicPlayer;
public PlayerHuman(int x, int y) {
super(x, y);
try {
basicPlayer = ImageIO.read(new File("Images/Player.png"));
} catch (IOException e) {
e.printStackTrace();
}
}
public void draw(Graphics2D g2d) {
g2d.drawImage(getPlayerImage(), x, y, null);
}
public BufferedImage getPlayerImage() {
return basicPlayer;
}
}
All help is appreciated.
EDIT:
My Apologies
GlobalPosition Class to give the player a starting point:
public class GlobalPosition {
public int x;
public int y;
public GlobalPosition() {
x = y = 0;
}
public GlobalPosition(int _x, int _y) {
x = _x;
y = _y;
}
public int getX() {
return x;
}
public int getY() {
return y;
}
public void setX(int newX) {
x = newX;
}
public void setY(int newY) {
y = newY;
}
}
I have a Game Loop class that repaints:
public class GameLoop implements Runnable {
GameWindow gWindow;
public GameLoop(GameWindow newGWindow) {
this.gWindow = newGWindow;
}
#Override
public void run() {
gWindow.repaint();
}
}
Suggestions:
Start small: test a very small program that does nothing but loads an image, puts it into an ImageIcon and displays the ImageIcon in a JOptionPane. Solve that first, and only then work on using it in the larger application.
You're better off reading the image in as a URL via getClass().getResource("....") than as a File.
don't call setVisible(true) on the top-level window until after adding all components.
Override the JComponent's paintComponent(Graphics g) method, not its paint method.
e.g.,
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.net.URL;
import javax.imageio.ImageIO;
import javax.swing.*;
public class ShowImage {
// ******* add the path to the image resource below *****
private static final String IMAGE_RESOURCE_PATH = "";
public static void main(String[] args) {
URL imgUrl = ShowImage.class.getResource(IMAGE_RESOURCE_PATH);
BufferedImage img;
try {
img = ImageIO.read(imgUrl);
if (img == null) {
String message = "Image cannot be found at \""
+ IMAGE_RESOURCE_PATH + "\"";
JOptionPane.showMessageDialog(null, message);
System.exit(-1);
}
Icon icon = new ImageIcon(img);
JOptionPane.showMessageDialog(null, icon);
} catch (IOException e) {
e.printStackTrace();
}
}
}
Turns out that it was the
setVisible(true)
call that was causing the problem. I pushed it below all the other calls in the method and the screen and image appeared. Thanks for the help.
EDIT:
#Sage this is the location so the location is correct, however I will begin to use the
getClass().getResource(..)
method from now on.
How do I get my image to follow my mouse anywhere on the screen?
The below code makes the image move along the x axis.
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
public class PlayerTwo implements KeyListener, MouseListener, MouseMotionListener{
public static int PLAYER_HEIGHT = 15;
public static int PLAYER_WIDTH = 15;
private Image p2Image = null;
private static int x = 0;
private static int y = 0;
private int heightPosition = 0;
Main main = null;
public PlayerTwo(Image pi, Main m ){
main = m;
p2Image = pi;
y = (int)((Main.WIDTH*2)+(PLAYER_WIDTH*2));
heightPosition = Main.HEIGHT-PLAYER_HEIGHT-20;
}
public void drawPlayer(Graphics g){
g.drawImage(p2Image, y, heightPosition, main);
}
public void keyTyped(KeyEvent e) {
}
public void keyReleased(KeyEvent e) {
}
public void mouseClicked(MouseEvent e) {
}
public void mousePressed(MouseEvent e) {
}
public void mouseReleased(MouseEvent e) {
}
public void mouseEntered(MouseEvent e) {
}
public void mouseExited(MouseEvent e) {
}
public void mouseDragged(MouseEvent e) {
}
public void mouseMoved(MouseEvent me) {
int newX = me.getX();
int newY = me.getY();
if(newY > (Main.HEIGHT+PLAYER_HEIGHT+10)){
y = Main.HEIGHT+PLAYER_HEIGHT+10;
}else{
y = newY;
}
// if (newX > (Main.WIDTH-PLAYER_WIDTH-10)){
// x = Main.WIDTH-PLAYER_WIDTH-10;
// }else{
// x = newX;
// }
}
}
Updated with Main...
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.image.BufferedImage;
import javax.swing.JFrame;
public class Main extends JFrame implements Runnable {
public static int WIDTH = 600;
public static int HEIGHT = 600;
private int gameSpeed = 100;
PlayerOne playOne = null;
PlayerTwo playTwo = null;
Image p1Image = null;
Image p2Image = null;
Image backImage = null;
Graphics offscreen_high;
BufferedImage offscreen;
public Main(String frameTitle) {
super(frameTitle);
p1Image = new javax.swing.ImageIcon("src/resources/player1.gif").getImage();
p2Image = new javax.swing.ImageIcon("src/resources/player2.gif").getImage();
backImage = new javax.swing.ImageIcon("src/resources/back.png").getImage();
offscreen = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_RGB);
offscreen_high = offscreen.createGraphics();
playOne = new PlayerOne(p1Image, this);
playTwo = new PlayerTwo(p2Image, this);
addKeyListener(playOne);
addKeyListener(playTwo);
addMouseListener(playTwo);
addMouseMotionListener(playTwo);
setSize(WIDTH, HEIGHT);
setVisible(true);
startGame();
}
public void startGame() {
Thread thread = new Thread(this);
thread.start();
}
public void paint(Graphics g) {
offscreen_high.setColor(Color.black);
offscreen_high.fillRect(0, 0, WIDTH, HEIGHT);
offscreen_high.drawImage(backImage, 0, 0, this);
playOne.drawPlayer(offscreen_high);
playTwo.drawPlayer(offscreen_high);
g.drawImage(offscreen, 0, 0, this);
}
// public void update(Graphics g){
// paint(g);
// }
public void run() {
int count = 0;
while (true) {
try {
Thread.sleep(gameSpeed);
} catch (InterruptedException ie) {
}
repaint();
count++;
}
}
public static void main(String[] args) {
Main main = new Main("Game On!");
}
}
Generally, you need some way to tell the UI that it should be updated.
Assuming that Main is some kind of component (and it's also responsible for painting the Player), you should be calling its repaint method in the mouseListener
But without more details, this is more of a guess
Updated
After a muck around with the code, the main problem, as I see it, is your trying to draw the image only the horizontal axis (x) using the vertical position (y)...
public void drawPlayer(Graphics g){
//g.drawImage(p2Image, y, heightPosition, main);
g.drawImage(p2Image, x, heightPosition, main);
}
To get it to work, you're going to have to uncomment the code in you mouseMoved method so that the x position updates.
You should also avoid painting to top level containers, the main reason (apart from the fact that you can screw up the paint process) is that top level containers are not double buffered.
Instead, you should move your entire game container over to something like a JPanel and override it's paintComponent method (and don't for get to call super.paintComponent)