I have been practicing with java's swing features recently, and in one of my classes that extends the class JPanel, I have overriden the method paintComponent() so that it will paint my BufferedImage onto the JPanel. I also have a method on it to move around. Before this issue, I have had a problem that shows the process of moving as it repaints too quickly. So, I created a boolean variable called available which is set to false when the image is still in the moving process. But, now I see that the screen is taking away the entire image and putting it back, causing it to blink. Here is my basic pseudocode:
class A extends JPanel{
BufferedImage canvas;
public A(){
//create image here
}
public move(){
available = false;
//move things around in here
available = true;
}
#Override
protected void paintComponent(Graphics g){
super.paintComponent(g);
if(available){
g.drawImage(this.canvas, 0, 0, null);
}
g.dispose();
}
}
class B{
public static void main(String[] args){
//construct the class A JPanel
while(some_variable){
class_A_JPanel.repaint();
}
}
}
This is very old topic which is fixed in modern Java. But you prefer old way then use old techniques. For example Double Buffering
Related
I'm working on a vertical scrolling game, and I'm using a thread to generate new enemies every 2 seconds. Each enemy is an image in a JPanel. For some reason, The generated enemies are not showing up in the JFrame, but they are present. When the player collides with one of the enemies, all the enemies show up.
Here's the code:
private void checkCollision() {
for(AlienShip as : enemies) {
if(player.getBounds().intersects(as.getBounds()))
player.setVisible(false);
}
}
private void setAlien() {
alien = new AlienShip();
add(alien);
enemies.add(alien);
System.out.println("Enemies: " + enemies.size());
}
public Thread alienGenerator() {
for(int i = 0; i < 3; i++) { // these are being drawn
setAlien();
}
return new Thread(new Runnable() {
#Override
public void run() {
int sleepTime = 2000;
while(true) {
try {
Thread.sleep(sleepTime);
} catch(InterruptedException e) {
System.out.println(e);
}
setAlien(); //these aren't
}
}
});
}
private void gameLoop() {
alienGenerator().start();
mainTimer = new Timer(50, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
repaint();
checkCollision();
}
});
mainTimer.start();
}
It always seems that you're Darned If You Do And Darned If You Don't. As far as I'm concerned the code you had placed in your earlier post was adequate, as a matter of fact, it was still lacking (no PlayerShip Class). The code example in this post does even less justice. Never the less......
Before I get started I just want you to know that I personally would have tackled this task somewhat differently and the meager assistance provided here will be solely based on the code you have already provided in this and previous posts.
The reason you are not seeing your Alien Ships displaying onto the Game Board upon creation is because you don't revalidate the board panel. As you currently have your code now this can be done from within the Board.setAlien() method where the Alien Ships are added. Directly under the code lines:
alien = new AlienShip();
add(alien);
enemies.add(alien);
add the code line: revalidate();, so the code would look like this:
alien = new AlienShip();
add(alien);
enemies.add(alien);
revalidate();
Your Alien Ships should now display.
On A Side Note:
What is to happen when any Alien Ship actually makes it to the bottom of the Game Board? As a suggestion, have them re-spawn to the top of the game board (serves ya right fer missin em). This can be done from within the AlienShip.scrollShip() method by checking to see if the Alien Ship has reached the bottom of the board, for example:
public void scrollShip() {
if (getY() + 1 > this.getParent().getHeight()) {
setY(0 - PANEL_HEIGHT);
}
else {
setY(getY() + 1);
}
}
In my opinion, PANEL_HEIGHT is the wrong field name to use. I think it would be more appropriate to use something like ALIEN_SHIP_WIDTH and ALIEN_SHIP_HEIGHT. Same for the variables panelX and panelY, could be alienShipX and alienShipY. Food for thought.
As you can see in the code above the current Game Board height is acquired by polling the Game Board's getHeight() method with: this.getParent().getHeight(). This allows you to change the Game Board size at any time and the Alien Ships will know where that current boundary is when scrolling down. All this then means that the setResizable(false); property setting done in the Main Class for the Game's JFrame window can now be resizable: setResizable(true);.
You will also notice that when the Alien Ship is re-spawned at top of the Game Board it is actually out of site and it flows into view as it moves downward. I think this is a much smoother transition into the gaming area rather than just popping into view. This is accomplished with the setY(0 - PANEL_HEIGHT); code line above. As a matter of fact even when the game initially starts, your Alien Ships should flow into the the gaming area this way and that can be done from within the AlienShip.initAlienShip() method by initializing the panelY variable to panelY = -PANEL_HEIGHT;.
This now takes me to the initialization of the PANEL_WIDTH and PANEL_HEIGHT fields. The values seem enormous (224 and 250 respectively). Of course you may have set to these sizes for collision testing purposes, etc but I think an image size of 64 x 35 would most likely suffice:
This image should be a PNG image with a transparent background which then eliminates the need for the setBackground(Color.BLUE); code line located within the AlienShip.initAlienShip() method.
The AlienShip.getX() and AlienShip.getY() methods should be overridden:
#Override
public int getX() { ... }
#Override
public int getY() { ... }
I think extending the AlienShip Class to JLabel would be better than to JPanel. To JPanel seems like overkill:
public class AlienShip extends JLabel { ... }
Adding a background image to the Game Board can add pizazz to the game. This can be achieved by adding the following code to the Board.paintComponent() method:
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
ImageIcon imgIcon = new ImageIcon("images/StarBackground.png");
Image img = imgIcon.getImage();
g.drawImage(img, 0, 0, this.getSize().width, this.getSize().height, this);
}
Images can be acquired here.
This should keep you going for a while. Before to long it'll be Alien mayhem.
Problem:
Main.repaint() doesn't work for me. repaint() doesnt invoke my paint method in Main. I've tried calling validate before repainting but with no succes. Main paints perfectly initially or when resized but when i call repaint() in my code nothing is happening.
Here is how the program looks so far link
So im trying to create a level selection screen for a game in java. My game is a JApplet. I have a structure as follows:
my Main class which extends JApplet and contains an object of
LevelScreen class
LevelScreen has a paint method which Main invokes.
I tried to avoid using Swing since the layout managers gave me trouble with the design. So I've tried to make a structure which were simpler and more suited for my need.
paint() in Main.java
public class Main extends JApplet {
public static final int WIDTH = 700, HEIGHT = 500;
private static Main instance;
private LevelScreen levelScreen = new LevelScreen();
private View view = View.LEVELSCREEN;
public static Main getInstance() {
if (instance == null)
instance = new Main();
return instance;
}
#Override
public void init() {
setSize(WIDTH, HEIGHT);
addMouseMotionListener(new MouseAdapter() {
#Override
public void mouseMoved(MouseEvent e) {
Point p = e.getPoint();
if (view == View.LEVELSCREEN) {
levelScreen.mouseMoved(p);
}
}
});
}
#Override
public void paint(Graphics g) {
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
if (view == View.LEVELSCREEN)
levelScreen.paint(g2);
}
public enum View {
GAME, LEVELSCREEN;
}
}
In the code of my Buttons i try to repaint Main because i want to make a fade out animation when mouse leaves the button. my problem is that i cant invoke the paint(Graphics g) in main with repaint()
Here i call repaint():
public void mouseExited() {
//start new thread to make fade out animation when mouse leave
mouseOver = false;
TimerTask task = new TimerTask() {
#Override
public void run() {
while (!mouseOver && opacity > 0.6) {
opacity -= 0.02;
//set level to 999 so i can see if the game repaints()
level = 999;
Main.getInstance().repaint(); //this doesnt work!!
try {
Thread.sleep(20);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
};
new Thread(task).start();
}
This is a problem with the way that you implement the singleton design pattern. The way you do it doesn't work for an applet, where the instance is created for you by the applet container. You can fix it by changing getInstance as follows:
public Main getInstance() {
return instance;
}
And add this line to the init method:
instance = this;
By the way, you should not override paint in a Swing component, which a JApplet is. You should override paintComponent instead, and call super.paintComponent(g) as the first line. This should fix the problem.
Main.getInstance().repaint(); //this doesnt work!!
I'm not surprised. You're not the one creating the instance of the JApplet, the browser is.
When you call this...
public static Main getInstance() {
if (instance == null)
instance = new Main();
return instance;
}
You are actually creating a second instance of the applet, which is NOT the one that is on the screen, so when you call repaint, Swing goes, "no point, you're not even displayable" and does nothing.
Without any more context of you code, you may not even need getInstance, instead reference the current instance using Main.this instead.
You should also consider taking a look at Performing Custom Painting.
Top level containers like JAppelt are not double buffered, which involves more work to paint directly to them. Instead, move your application to be based on something like a JPanel and override it's paintComponent method instead.
Painting is also a complex, multi-layered scheme. You MUST call super.paintXxx in order to preserve the paint chain and prevent any possible issues.
I am making a game in Java and I have a canvas class, which has the game tick, and I draw the images on the canvas using
public void paint(Graphics g) {
// draw stuff here
}
I want to move all the drawing functions to my Engine class.
I have this method in my Engine:
#Override
public void render(Graphics scene) {
// draw stuff here
}
In my Canvas I didn't have to call the paint method, but in the Engine I would have to call the render method, but since it takes as an argument Graphics scene, I am kind of at a loss. How would I be able to draw components from my Engine class(using the render method) and not from the Canvas class.
The engine class does not extend any JComponent, but it does initialize the Canvas object
I am making a game in Java and I have a canvas class, which has the
game tick, and I draw the images on the canvas using
note
public void paint(Graphics g) { for awt.Canvas, awt.Panel
public void paintComponent(Graphics g) { for swing.JComponent, swing.JPanel
any painting could be done only for J/Component, good practicies couldn't be move this methods outside of J/Component declarations
I want to move all the drawing functions to my Engine class. I have
this method in my Engine:
is good idea to prepare all Object before paint/paintComponent is executed,
there to put all Objects to the array
inside paint/paintComponent only loop inside arrays of prepared Obects, invoked from Swing Timer, Mouse/Key events
all events for paiting, to AWT/Swing GUI must be done on Event Dispatch Thread
for todays GUI to use Swing JComponent, JPanel and override paintComponent
a few very good code examples are here, tagged by paintComponent
One problem is that there is no way for Engine to know that paint(g) is called, since the call is run on the canvas. So you will most likely need to modify Canvas.paint so that it calls the Engine. It's possible there is some funky listener you could add to Canvas but I think that's probably more complicated than you need.
One way that would work would be to pass an instance of Engine to the Canvas on construction:
public class Engine {
private Canvas canvas;
public Engine() {
Canvas = new Canvas(this);
}
public void render(Graphics g) {
// do stuff
}
}
public class Canvas extends JPanel {
private Engine engine;
public Canvas(Engine engine) {
this.engine = engine;
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
engine.render(g);
}
}
ok i have two classes similar like this(the graphics are set up the same way) and another class that is displayed on the bottom. as you can see i have two graphics2ds that i would like to display at the same time with the items class being transparent and on top (the items class has almost nothing in it, and the game class is fully covered with pictures and such)
is there any way to do this?
currently the items class take priority ever the game class because it was called last and totally blocks the game class.
public class game extends Canvas implements Runnable
{
public game()
{
//stuff here
setBackground(Color.white);
setVisible(true);
new Thread(this).start();
addKeyListener(this);
}
public void update(Graphics window)
{
paint(window);
}
public void paint(Graphics window)
{
Graphics2D twoDGraph = (Graphics2D)window;
if(back==null)
back = (BufferedImage)(createImage(getWidth(),getHeight()));
Graphics graphToBack = back.createGraphics();
//draw stuff here
twoDGraph.drawImage(back, null, 0, 0);
}
public void run()
{
try
{
while(true)
{
Thread.currentThread();
Thread.sleep(8);
repaint();
}
}catch(Exception e)
{
}
}
}
class two
public class secondary extends JFrame
{
private static final int WIDTH = 800;
private static final int HEIGHT = 600;
public secondary()
{
super("Test RPG");
setSize(WIDTH,HEIGHT);
game game = new game();
items items = new items();
((Component)game).setFocusable(true);
((Component)items).setFocusable(true);
getContentPane().add(game);
getContentPane().add(items);
setVisible(true);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
public static void main( String args[] )
{
secondary run = new secondary();
}
}
Here are my suggestions:
Extend JComponent rather than Canvas (you probably want a lightweight Swing component rather than a heavyweight AWT one)
Then don't bother with the manual back-buffering for your drawing - Swing does back-buffering for you automatically (and will probably use hardware acceleration while doing so)
Have one component draw both items and the rest of the game background. There is no good reason to do it separately (even if you only change the items layer, the background will need to be redrawn because of the transparency effects)
Capitalise Your ClassNames, it makes my head hurt to see lowercase class names :-)
EDIT
Typically the approach would be to have a class that represents the visible area of the game e.g. GameScreen, with a paintCompoent method as follows:
public class GameScreen extends JComponent {
....
public void paintComponent(Graphics g) {
drawBackground(g);
drawItems(g);
drawOtherStuff(g); // e.g. animated explosions etc. on top of everything else
}
}
I'm writing a simple game. I have 3 classes the first one: ball which take care of every thing refereed to it, the second one game which made out of an array of "ball"s and the final one is windows, the one which contains the MAIN thread.
window.paint calls game.draw in order to receive the graphics of the game scene.While the game itself double buffering it in order that the Image object can be moved to the Player's ball location(yet to be implemented).
So my problem caused because I'm creating an Image object but somewhy this initialized to null, thus I get NullPointerException.
Here is the source of the methods which handle the painting:
public class MyWindow extends JFrame {
//...the other code
public void paint(Graphics g){
thegame.draw();
repaint();
}
}
public class Game extends JFrame implements Runnable {
ball[] cellmap;
//...the other code
public void draw(){
Image GameImage = createImage(800,800);
Graphics GameGraphics = GameImage.getGraphics();
for(int i = 0;i<cellmap.length;i++)
cellmap[i].draw(GameGraphics);
g.drawImage(GameImage, 0, 0, this);
}
}
public class Ball extends JFrame {
//...the other code
public void draw(Graphics g){
g.setColor(Color.red);
g.fillOval((int)(this.x+this.radious),(int)(this.y+this.radious),
(int)this.radious,(int)this.radious);
}
}
1) please read Java Naming Conventions
2) not good idea paint directly to the JFrame, put your painting to the JComponent, JLabel, JPanel
3) for Painting in Swing use method paintComponent, please not methods paint(Graphics g) or draw(Graphics g)
4) if you want to delay or animate you painting use javax.swing.Timer