Why Doesn't My Mario Sprite (a JComponent) Show Up? [duplicate] - java

This question already has answers here:
How to Draw an BufferedImage to a JPanel
(2 answers)
Closed 5 years ago.
I'm trying to write a Mario 3 level 1 clone, and previously, I was able to get it to show up in JFrames when I had the pulling of the sprite in the constructor of Mario itself. However, because I realized that I'll need to link things like the coordinates and state (small, large) to the moving image itself, I decided to tear it up and have a Mario and MarioComponent. I have a MarioComponent class but it doesn't show up.
public class MarioComponent extends JComponent{
private Mario m;
public MarioComponent(){
m = new Mario();
}
public void paintComponent(Graphics g){
Graphics2D g2 = (Graphics2D) g;
super.paintComponent(g2);
m.draw();
}
public void moveMario(){
m.move();
repaint();
}
public static void main(String[] args){
JFrame f = new JFrame();
f.setSize(868,915);
MarioComponent m = new MarioComponent();
m.setLocation(100,100);
f.add(m);
f.setVisible(true);
f.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
}
}
my Mario class:
public class Mario{
//all numbers multiplied by 2 from OG game
protected MarioState state;
protected int x, y;
BufferedImage sprite;
public Mario(){
this.state = MarioState.SMALL;
this.x = 54;
this.y = 806;
}
public Mario(MarioState s, int x, int y){
this.state = s;
this.x = x;
this.y = y;
}
public void move(){
this.x+=2;
}
public void jump(){
this.y -= 46;
}
public String getCoordinates(){
return "Mario coords: " + this.x + ", " + this.y + ".";
}
public void draw(){
URL spriteAtLoc = getClass().getResource("sprites/Mario/SmallStandFaceRight.bmp");
try{
sprite = ImageIO.read(spriteAtLoc);
} catch(IOException e){
System.out.println("sprite not found");
e.printStackTrace();
}
}
}
When I try to place a MarioComponent on a JFrame, its not there. How can I remedy this?

You need to use the Graphics object, g (or g2 -- they're the same object) within your paintComponent as the "pen" with which to draw. You don't do this, so there's no way that anything could or should render. Graphics has a method, g.drawImage(...) that accepts an image as one of its parameters and would be the best way for this to work.
Suggestions:
Change draw so that it accepts a Graphics parameter: public void draw(Graphics g){
Within paintComponent, pass the Graphics parameter given to the method by the JVM into your draw call: m.draw(g);
Don't keep re-reading in the image within the draw method. This is wasteful and will only cause lag. Read it in once and store it into a variable.
All the code within the current draw method shouldn't be there. Again, the image should be read in once, likely within the constructor, and draw should instead actually draw the image, not read the file.
Don't guess at this, but rather read the appropriate tutorials
Lesson: Performing Custom Painting: introductory tutorial to Swing graphics
Painting in AWT and Swing: advanced tutorial on Swing graphics

Related

Java: JFrame Graphics not drawing rectangle

Hello fellow programmers,
I've ran into a little issue in my code that I can't seem to crack. It has to do with the Jframe; Graphics area of Java. The code that I'll post below, is over a drawing method. Which purpose is to draw the "rooms" that are in a ArrayList roomList which is located in another class hence lvl. before. This off-course doesn't happen, hence the post on here.
public class LevelGUI implements Observer {
private Level lv;
private Display d;
public LevelGUI(Level level, String name) {
this.lv = level;
JFrame frame = new JFrame(name);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
d = new Display(lv, 500, 500);
frame.getContentPane().add(d);
frame.pack();
frame.setLocation(0, 0);
frame.setVisible(true);
}
private class Display extends JPanel {
public Display(Level fp, int x, int y) {
addKeyListener(new Listener());
setBackground(Color.GRAY);
setPreferredSize(new Dimension(x + 20, y + 20));
setFocusable(true);
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
draw(g);
}
private void draw(Graphics g) {
Level lvl = new Level();
for(int i = 0; i < lvl.roomList.size(); i++) {
Room room = lvl.roomList.get(i);
g.setColor(room.floorColor);
g.drawRect(room.posX, room.posY, room.roomWidth, room.roomHeight);
}
}
}
}
To get some background info on the program. roomList is the ArrayList, and it is filled with various different sized and colored rooms. The rooms themselves are objects.
Here comes first Level class:
public class Level extends Observable {
private boolean Switch = true;
public ArrayList<Room> roomList = new ArrayList<Room>();
(...)
}
Here is the Class Room() that is used to create the rooms.
public class Room {
Color floorColor;
int roomWidth;
int roomHeight;
int posX;
int posY;
public Room(int dx, int dy, Color color) {
this.floorColor = color;
this.roomHeight = dy;
this.roomWidth = dx;
this.posY = 0;
this.posX = 0;
}
(...)
}
I've managed to locate where the problem is thought to occur, and it's the code in the for-loop. I tried switching the roomList.size() for an integer to test if it was the loop., But it wasn't. It is possible to draw a figure outside of the for-loop.
and again, the problem isn't an error message, the program simply doesn't draw the rooms that I've instructed it to draw in the method draw().
The display output looks like this:
Thanks beforehand!
Be aware that the paintComponent() method is invoked by Swing whenever the framework thinks the component needs to be rendered on screen. This usually is when the window is getting visible - initially or because some other window no longer hides the component. Such events are out of your control.
So your application should create a state and be ready to draw it anytime. Therefore you do not create state (like a level) inside the paint() or paintComponent() method. Put that elsewhere - if need be into the constructor.
Looking at you code:
As you are creating a new level inside paintComponent()/draw(), is it correct to assume that this level has no rooms associated? In that case the method is right to return without having painted anything.
If your application thinks the screen should be updated call repaint(), knowing that the paint() method will be called by the framework soon.

How to put an picture to an object in Java?

Well I try to make an object an image, an object that goes into an ArrayList. But I don't know how to do it. I tried several methods but none of them I tried did not work. What do you think should be added? And my object is called Apple.Thanks in advance
public class Gamepanel extends JPanel {
public void tick()
{
if(apples.size()==0)
{
//System.out.println(apples.size());
int xC=r.nextInt(79);
int yC=r.nextInt(79);
apple=new Apple(xC,yC,10);
apples.add(apple);
}
for(int i=0;i<apples.size();i++)
{
if(xC==apples.get(i).getxC()&&yC==apples.get(i).getyC())
{
size++;
score++;
apples.remove(i);
i++;
}
}
}
public void paint(Graphics g)
{
//here I draw the snake and his food
if(State==STATE.GAME)
{
g.clearRect(0, 0, WIDTH, HEIGHT);
g.setColor(Color.BLACK);
g.fillRect(0,0,WIDTH,HEIGHT);
//here i make my map of the game
for(int i=0;i<apples.size();++i)
{
apples.get(i).draw(g);
}
}
public class Apple {
private int xC,yC,width,height;
BufferedImage ap=null;
public Apple(int xC,int yC,int titleSize) {
this.xC=xC;
this.yC=yC;
width=titleSize;
height=titleSize;
}
//here i want to draw a picture for each my objects from ArrayList
public void draw(Graphics g)
{
}
I'm not sure what you're meaning by make an object an image... I don't see any code with the definition for apple...
Have you tried using Textures? You can easily create a Texture[] array containing different textures for use.
Texture[] apples = new Texture[2];
apples[0] = new Texture("apple1.png");
apples[1] = new Texture("apple2.png");
ArrayLists can also be used, but my experience with 2d games is in using libGDx (im guessing your package is graphics2D? with GDx you can...
SpriteBatch batch = new SpriteBatch();
ArrayList<Texture> apples = new ArrayList<Texture>();
//add your textures, then draw using a SpriteBatch
batch.begin();
batch.draw([texture],xpos,ypos); batch.draw(apples.get(i), 150, 500);
batch.end();

Flickering white on canvas

I figured out a solution minutes after posting, but due to low reputation I couldn't delete the post
For the fun of it I decided to start working on something which might turn into a game at some point.
I'm trying to draw some circles and move them in a given direction currently. This causes flickering. It's very likely that I oversee something very basic but I can't figure out why it doesn't render smoothly.
My board class looks something like (removed what I deemed unnecessary):
public class Board extends Canvas implements Runnable {
public static void main(String[] args) {
Board board = new Board();
board.setPreferredSize(new Dimension(WIDTH * SCALE, HEIGHT * SCALE));
JFrame frame = new JFrame("Circles");
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
frame.add(board);
frame.pack();
frame.setVisible(true);
board.start();
}
#Override
public void run() {
while (running) {
process();
repaint();
try {
Thread.sleep(15);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
The paint method:
public void paint(Graphics g1) {
super.paint(g1);
Graphics2D g = (Graphics2D) g1;
for (Ball ball : Ball.BALLS) {
g.drawOval((int) ball.getLocation().getX(), (int) ball.getLocation().getY(), ball.getRadius(), ball.getRadius());
}
}
My process method:
private void process() {
if (utilities.randInt(1, 100) < 10 && Ball.getBallCount() < 40) {
Ball.spawnNew(this);
}
for (Ball ball : Ball.BALLS) {
ball.move(this);
}
Ball.BALLS.removeAll(Ball.TO_REMOVE);
Ball.TO_REMOVE.clear();
}
The move method basically increments the x-value of the ball by a given value each time its called (moving it right).
Like I said, I'm unsure why it flickers so if you have any pointers please do tell.
Thanks!
This sounds like a case where you need to perform double-buffering, so that one copy of your canvas can remain shown while you are updating the other.
You're using AWT here, and I don't know how to implement double-buffering manually with AWT. However, if you're willing to use Swing here you can take advantage of automatic double-buffering. See the question about How to make canvas with Swing? as well as Oracle Technology Network's article on Painting in AWT and Swing.
The basic idea would be:
extend javax.swing.JPanel instead of Canvas (which means when you override paint(Graphics) you're now overriding it from javax.swing.JComponent instead of java.awt.Component)
create a constructor with super(true) to enable double-buffering.
Edit: Also, as iccthedral points out, you're better off overriding paintComponent(Graphics) and including a call to super.paintComponent(Graphics). See Difference between paint, paintComponent and paintComponents in Swing.
You need double buffering. To do this you need to create a BufferedImage and get the Graphics from it. Paint everything to the image, render the image on to the screen, then finally fill the image with a the background color or image to reset it.
Sample:
//one time instantiation
BufferedImage b = new BufferedImage(width, height, mode);
In paint(Graphics g):
Graphics buffer = b.getGraphics();
//render all of the stuff on to buffer
Graphics2D g = (Graphics2D) buffer;
for (Ball ball : Ball.BALLS) {
g.drawOval((int) ball.getLocation().getX(), (int) ball.getLocation().getY(), ball.getRadius(), ball.getRadius());
}
g1.drawImage(b, 0, 0, width, height, null);
g.setColor(Color.BLACK);
//reset the image
g.drawRect(0, 0, width, height);
g.dispose();

getGraphics from panel returns null

so I have class Board that extends JApplet and in it's constructor I make a JPanel that I'll later draw boxes on, but when I try to do getGraphics it returns null :/
JPanel panel;
public Board(int x, int y, int wolfNumber, int hareNumber){
this.x=x;
this.y=y;
wolvesCoords = new int[wolfNumber][2];
haresCoords = new int[hareNumber][2];
panel = new JPanel();
panel.setVisible(true);
add(panel);
}
public synchronized void write(int xx, int yy, Color c){
int width=panel.getWidth()/x;
int height=panel.getHeight()/y;
Graphics g = panel.getGraphics();
System.out.println(g);
g.setColor(c);
g.drawRect(xx*width, yy*height, width, height);
g.fillRect(xx*width, yy*height, width, height);
}
public void paint(Graphics g)
{
super.paint(g);
}
It gives nullpointerexception at line g.setColor(c) as g is null.
You are using the Graphics object wrong. Instead of calling write from wherever you call it, instead override paintComponent. You could do something like:
private int xx;
private int yy;
private Color c;
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if(c != null) {
int width=panel.getWidth()/x;
int height=panel.getHeight()/y;
g.setColor(c);
g.drawRect(xx*width, yy*height, width, height);
g.fillRect(xx*width, yy*height, width, height);
}
}
public void write(int xx, int yy, Color c) {
this.xx = xx;
this.yy = yy;
this.c = c;
repaint();
}
Yours is a common problem and question and is yet another reason why you shouldn't use a Graphics object obtained by calling getGraphics() on a component. Another reason you shouldn't do this is that if you are successful at getting a non-null Graphics object (which is only available after the component has been rendered), it will not persist, and your image can turn null if any repaints occur.
Instead do what the tutorials advise you to do: Draw with the Graphics object provided to you in the JPanel's paintComponent method. If you want to draw a fixed background, then do so in a BufferedImage, and then draw that BufferedImage in the paintComponent method.
Edit
You ask:
Why would I call drawing code in the paint method? I need to draw only when the method write is called, not when the app starts.
Because that is how Swing graphics is done, because doing it your way is rife with problems (which you're already experiencing). Again, don't guess at this stuff -- read the tutorials where it is all well explained for you.
Edit
You state in comment:
Actually this error shows up when I try to add override - method does not override or implement a method from a supertype. Could it be that I am extending JApplet?
Yes, exactly so.
I have to though
Yes, you have to have a class that extends JApplet in order to produce JApplets, but you don't have to and in fact shouldn't paint directly in them. Instead create a separate class that extends JPanel, and do your graphics inside of that class's paintComponent method. Then display that JPanel in your applet.

Java - graphics paint

I'm trying to develop a Java brick breaker (like DxBall) game and I want to make the Ball object with its own draw method.
What I'm trying to do:
public class Ball {
private int x, y, diameter;
public void Ball(){
x = 0;
y = 0;
diameter = 20;
}
public void draw(Graphics g){
g.setPaint(Color.red);
g.fillOval(x, y, diameter, diameter);
}
}
Therefore, my game engine extends JFrame and its paintComponent method will call game objects draw method. To sum, is it proper way to do object oriented game in Java? What should my Ball class extend?
Your Ball class seems to look ok. It doesn't need to extend anything. You will need to pass the Graphics object from the paintComponent of your game object to the Ball draw method.
If you wish to make Ball a graphical component, you could extend JComponent:
public class Ball extends JComponent {
private int x;
private int y
private int diameter;
public Ball() {
x = 0;
y = 0;
diameter=20;
}
#Override
public void paintComponent(Graphics g){
super.paintComponent(g);
g.setPaint(Color.red);
g.fillOval(x, y, diameter, diameter);
}
}
and simply call repaint when you wish to paint the component instead of a custom draw method.
Note: There's no return type for constructors.
Your class is fine but I recommend extending a class. This class usually called as Sprite or Action or GameObject contains the basic information like
image (or animation), position, collision rect and some basic functions like get and set it's position, speed and some collision detection functions if you wish.
Some resources.
The Breakout game
GEJ - game-engine-for-java
Java-Gaming.org - Java Gaming Resources
Hope they help. And to draw the object, do
g.drawImage(ball.image, ball.x, ball.y, null);

Categories