Which layout manager to use on this game?
IMHO, using layouts and components is not a good solution to your problem, personally, I'd lean towards a custom painting solution instead.
Start with a basic concept of a piece, it needs to know it's location, it's size, it's color, be able to paint itself and possibly be relocatable, something like...
public interface Piece {
public Rectangle getBounds();
public Color getColor();
public void setLocation(Point point);
public void paint(Graphics2D g2d);
}
From this, you can define what ever shapes you need, for example...
public abstract class AbstractPiece implements Piece {
private Rectangle bounds;
private Color color;
#Override
public void setLocation(Point point) {
bounds.setLocation(point);
}
#Override
public Rectangle getBounds() {
return bounds;
}
#Override
public Color getColor() {
return color;
}
public void setBounds(Rectangle bounds) {
this.bounds = bounds;
}
public void setColor(Color color) {
this.color = color;
}
}
public class Square extends AbstractPiece {
public Square(Point location, int size, Color color) {
Rectangle bounds = new Rectangle();
bounds.setLocation(location);
bounds.setSize(size, size);
setBounds(bounds);
setColor(color);
}
#Override
public void paint(Graphics2D g2d) {
g2d.setColor(getColor());
g2d.fill(getBounds());
g2d.setColor(Color.GRAY);
Rectangle bounds = getBounds();
g2d.drawLine(bounds.x + (bounds.width / 2), bounds.y, bounds.x + (bounds.width / 2), bounds.y + bounds.height);
g2d.drawLine(bounds.x, bounds.y + (bounds.height / 2), bounds.x + bounds.width, bounds.y + (bounds.height / 2));
}
}
This is just a basic square, nothing fancy, but, it's self contained, easy to create and manage. You can create any combination of shapes you like using this simple pattern, at the end of the day, your board class won't care, it just needs to the space it occupies and how to paint it, speaking for which, you need some kind of container to manage all these shapes...
public class PuzzelPane extends JPanel {
private List<Piece> pieces;
public PuzzelPane() {
Dimension size = getPreferredSize();
pieces = new ArrayList<>(25);
Point location = new Point((size.width - 50) / 2, (size.width - 50) / 2);
pieces.add(new Square(location, 50, Color.BLUE));
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
for (Piece piece : pieces) {
Graphics2D g2d = (Graphics2D) g.create();
piece.paint(g2d);
g2d.dispose();
}
}
}
This is a really simply concept, it has a List to maintain all the available shapes and simply loops over this to paint them in the paintComponent method
Couple it with the idea from this example and this example and you have the ability to now drag the shapes
To expand on kaetzacoatl's comment, you should not use a LayoutManager for this at all, for several reasons:
They do not have the flexibility for more complex setups.
They are quite hard to work with for non-trivial movement of elements.
They mostly make sense for elements that can be resized and maybe wrapped or stretched, which I assume does not make sense for tiles in a puzzle game.
Instead, I would advise to use something like a canvas and draw your puzzle pieces with coordinates.
Related
I am taking an introductory java class and I have an inheritance related problem. I have to implement a system that has classes for various figures. the superclass is Figures and subclasses are Circle, Box and Rectangle and I am supposed to output the figures in the JPanel. I have designed all the classes and my code is below but I need to define the center() method on the figure class which when called on an object should draw and also erase the object. so within the main method of the JPanel class, I want to use the center() method to draw the three shapes and use the wait(long timeout) method to make the shapes change to random positions after 15 seconds.
import java.awt.Graphics;
import javax.swing.JPanel;
public abstract class Figure extends JPanel {
protected int xCoord;
protected int yCoord;
public Figure() {
}
public void erase() {
}
public void draw() {
}
public void center() {
}
}
the rectangle sub class is:
import java.awt.Color;
import java.awt.Graphics;
import javax.swing.JPanel;
public class Rectangle extends Figure {
int width;
int height;
public Rectangle() {
this.width = 0;
this.height = 0;
}
public Rectangle(int xCoord, int yCoord, int width, int height)
{
this.xCoord = xCoord;
this.yCoord = yCoord;
this.width = width;
this.height = height;
}
public void draw(Graphics g) {
super.paintComponent(g);
g.setColor(Color.BLACK);
g.fillRect(xCoord, yCoord, width, height);
}
public void erase(Graphics g) {
super.paintComponent(g);
g.setColor(Color.BLACK);
g.fillRect(0, 0, 0, 0);
}
}
the circle subclass:
public class Circle extends Figure {
private int radius;
public Circle ()
{
this.radius = 0;
}
public Circle (int xCoord, int yCoord, int radius)
{
this.xCoord = xCoord;
this.yCoord = yCoord;
this.radius = radius;
}
public void draw(Graphics g){
super.paintComponent(g);
g.setColor(Color.BLACK);
g.fillOval(xCoord, yCoord, radius, radius);
}
public void erase(Graphics g){
super.paintComponent(g);
g.setColor(Color.BLACK);
g.fillOval(0,0,0,0);
}
}
the box subclass:
import java.awt.Color;
import java.awt.Graphics;
import javax.swing.JPanel;
public class Box extends Figure {
private int edgeLength;
public Box() {
this.edgeLength = 0;
}
public Box(int xCoord, int yCoord, int edgeLength)// Creating Rectangle
// Class given width and
// height
{
this.xCoord = xCoord;
this.yCoord = yCoord;
this.edgeLength = edgeLength;
}
public void draw(Graphics g) {
super.paintComponent(g);
g.setColor(Color.BLACK);
g.fillRect(xCoord, yCoord, edgeLength, edgeLength);
}
public void erase(Graphics g) {
super.paintComponent(g);
g.setColor(Color.BLACK);
g.fillRect(0, 0, 0, 0);
}
}
the JPanel class
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.LayoutManager;
import java.awt.Point;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class SamplePanel extends JPanel {
private static final int PANEL_HEIGHT = 400;
private static final int PANEL_WIDTH = 400;
Rectangle rect = new Rectangle(75, 85, 50,30);
Circle circle = new Circle(275, 75, 50);
Box box = new Box (75, 275, 50);
public SamplePanel() {
setPreferredSize(new Dimension(PANEL_HEIGHT, PANEL_WIDTH));
setBackground(Color.WHITE);
//Rectangle rect = new Rectangle(100, 100, 15,10);
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
rect.draw(g);
circle.draw(g);
box.draw(g);
g.drawLine(200, 0, 200, 400);
g.drawLine(0, 200, 400, 200);
}
/**
* #param layout
*/
public SamplePanel(LayoutManager layout) {
super(layout);
}
/**
* #param isDoubleBuffered
*/
public SamplePanel(boolean isDoubleBuffered) {
super(isDoubleBuffered);
}
/**
* #param layout
* #param isDoubleBuffered
*/
public SamplePanel(LayoutManager layout, boolean isDoubleBuffered) {
super(layout, isDoubleBuffered);
}
public static void main(String[] args) {
JFrame frame = new JFrame("Window Title Here");
SamplePanel panel = new SamplePanel();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(panel);
frame.pack();
frame.setVisible(true);
Figure box = new Box(75, 275, 50);
}
}
Answer...
How to use Swing Timers
It is safe to use within the context of Swing as it won't block the Event Dispatching Thread while waiting and will generate notifications within the context of the Event Dispatching Thread, making it safe to update the UI from.
Observations...
Your basic design, while correct in concept, is implemented in correctly. I'd would strongly discourage you from using JPanel in this way. Components have a particular life cycle which you're not managing correctly, which could cause no end of issues.
Instead, go right back to basics. What is it the shape should be able to?
public interface Figure {
public void draw(Graphics2D g2d);
public Rectangle getBounds();
public void setBounds(Rectangle bounds);
}
Here, it's capable of been painted and has a concept of size and position (yes, you can use an abstract class, but this removes all requirements on the base implementation, making it far more flexible)
Because I imagine most of the implementations are going to be basically the same, I can use an abstract class to implement the core functionality...
public abstract class AbstractFigure implements Figure {
private Rectangle bounds;
#Override
public Rectangle getBounds() {
return bounds;
}
#Override
public void setBounds(Rectangle bounds) {
this.bounds = bounds;
}
}
Based on your needs, you might be able to create a few abstract classes which implement the functionality different, but the basic idea is to see what functionality is common to all the implementations and reduce the amount of duplicate code you generate.
Then you can start implementing your concrete classes...
public class RectangleFigure extends AbstractFigure {
public RectangleFigure(Rectangle bounds) {
setBounds(bounds);
}
#Override
public void draw(Graphics2D g2d) {
g2d.setColor(Color.BLACK);
g2d.fill(getBounds());
}
}
But where's the "erase" method you ask? Well, erase is just the absence of painting, so, when you want to "erase" a shape, remove it from the painting process and repaint the container, shape is erased
This is suggests to me that you need to have a look at Painting in AWT and Swing and Performing Custom Painting to better understand how painting works in Swing.
"Where's the center method?" I hear you ask. Okay, you "could" have a center method in the Figure, the problem is, Figure has no concept of the parent container, is you'd have to pass the size of the container to it. Not difficult, I'd argue, however, this really isn't a function of the Figure, but a function of the container, so I'd have the center method implemented there, since you want to change the position of the Figures anyway, it makes sense (to me) to all that wrapped up in the container
I'm making a simple app that allows you to show different circle objects browse through them with buttons.
The problem is i have no idea how to print out my circleobjects into the jPanel.
When you first run the program the first circleobject should appear in the jPanel. Here's my circleclass:
public class Circle {
private int height;
private int width;
private Color color;
public Circle (int height, int width, Color color){
this.height = height;
this.width = height;
this.color = color;
}
public int getHeight() {
return height;
}
public void setHeight(int height) {
this.height = height;
}
public Color getColor() {
return color;
}
public void setColor(Color color) {
this.color = color;
}
public int getWidth() {
return width;
}
public void setWidth(int width) {
this.width = width;
}
}
And here is the first part of the GUI code. I've made 5 circleobjects in an arraylist.
public class CircleGUI extends javax.swing.JFrame{
public ArrayList<Circle> circles = new ArrayList<Circle>();
public CircleGUI(){
initComponents();
circles.add(new Circle(15, 15, Color.blue));
circles.add(new Circle(20, 15, Color.black));
circles.add(new Circle(30, 10, Color.green));
circles.add(new Circle(20, 10, Color.orange));
circles.add(new Circle(35, 35, Color.red));
}
Now, how do i make my first object appear in the jPanel which is marked on the screenshot?
You will have to override the paintComponent(Graphics) method of your JPanel.
Then you do the drawing on the Graphics object, based on the data your current Circle object contains.
Check out Custom Painting Approaches. It does exactly what you want (except it paint Rectangles).
It actually shows two approaches:
Painting objects from an ArrayList (which is what you want)
Painting objects onto a BufferedImage
The basic painting code in your case is:
#Override
protected void paintComponent(Graphics g)
{
super.paintComponent(g);
// Custom code to paint all the Rectangles from the List
for (DrawingArea.ColoredRectangle cr : coloredRectangles)
{
g.setColor( cr.getForeground() );
Rectangle r = cr.getRectangle();
g.drawRect(r.x, r.y, r.width, r.height);
}
}
Of course you would paint ovals and use your Circle class
So I've been working on a homework on abstraction for my programming class and fell into a problem. The goal for me right now is to be able to use abstraction, then later be able to draw with rectangles and ovals a simple city, like a rectangular building or a oval light on a light post.
The error I am receiving when I compile is: MyTestApp.Rectangle is not abstract and does not override abstract method drawEllipse(java.awt.Graphics) in MyTestApp.Shape. This Error shows up on the line "class Rectangle extends Shape{" right below the class Shape.
My question is what am I doing wrong with my abstraction? I've been messing with the constructors and draw() methods in classes Rectangle and Ellipse for a while now and still to no luck happen to find a solution.
Code is below:
import java.awt.*;
import javax.swing.*;
public class MyTestApp extends JPanel {
Rectangle rect;
Ellipse oval;
public static void main(String [] args) {
MyTestApp myTestApp = new MyTestApp ();
myTestApp.test();
}
public MyTestApp () { //creates the jframe
JFrame frame = new JFrame("MyClass Driver");
setBackground(new Color(200, 250, 200));
setPreferredSize(new Dimension(500, 400));
frame.add(this);
frame.pack();
frame.setVisible(true);
}
public void delay(int msecs) {
try {
Thread.sleep(msecs);
} catch (InterruptedException e) {
}
}
public void paint(Graphics g) {//paints the rectangle and ellipse
super.paint(g);
if (rect != null)
rect.drawRectangle(g);
if (oval != null)
oval.drawEllipse(g);
}
public void test() {//gives the x/y position, width/height, and fill/outline color for the rectangle and oval
delay(1000);
rect = new Rectangle(20, 30, 23, 75, Color.GREEN, Color.BLUE);
oval = new Ellipse(10, 10, 10 , 34, Color.RED, Color.MAGENTA);
repaint();
}
public abstract class Shape{//abstract class Shape that sets the x/y, width/height, and colors for the shapes
private int x, y, width, height;
private Color fillColor;
private Color outlineColor;
public Shape(int x, int y, int width, int height, Color fillColor, Color outlineColor) {
setXY(x, y);
setSize(width, height);
setFillColor(fillColor);
setOutlineColor(outlineColor);
}
public boolean setXY(int x, int y) {
this.x = x;
this.y = y;
return true;
}
public void setSize(int width, int height) {
if (width > 0)
this.width = width;
if (height > 0)
this.height = height;
}
public boolean setFillColor(Color fillColor){
if (fillColor == null) return false;
this.fillColor = fillColor;
return true;
}
public boolean setOutlineColor(Color outlineColor){
if (outlineColor == null) return false;
this.outlineColor = outlineColor;
return true;
}
public Color getFillColor() {
return fillColor;
}
public Color getOutlineColor() {
return outlineColor;
}
public abstract void drawRectangle(Graphics g);//do i need two?
public abstract void drawEllipse(Graphics g);//do i need both?
}
class Rectangle extends Shape{//!!!!!!!!!! where the error shows
public Rectangle(int x, int y, int width, int height, Color fillColor, Color outlineColor) {
super(x, y, width, height, fillColor, outlineColor);
}
public void drawRectangle(Graphics g){//draws the retangle
g.setColor(fillColor);
g.fillRect(x, y, width, height);
g.setColor(outlineColor);
g.drawRect(x, y, width, height);
}
}
class Ellipse extends Shape{
public Ellipse(int x, int y, int width, int height, Color fillColor, Color outlineColor) {
super(x, y, width, height, fillColor, outlineColor);
}
public void drawEllipse(Graphics g){//draws the ellipse
g.setColor(fillColor);
g.fillOval(x, y, width, height);
g.setColor(outlineColor);
g.drawOval(x, y, width, height);
}
}
}
Thanks for reading and helping!
Both classes Rectangle and Ellipse need to override both of the abstract methods.
To work around this, you have 3 options:
Add the two methods
Make each class that extends Shape abstract
Have a single method that does the function of the classes that will extend Shape, and override that method in Rectangle and Ellipse, for example:
abstract class Shape {
// ...
void draw(Graphics g);
}
And
class Rectangle extends Shape {
void draw(Graphics g) {
// ...
}
}
Finally
class Ellipse extends Shape {
void draw(Graphics g) {
// ...
}
}
And you can switch in between them, like so:
Shape shape = new Ellipse();
shape.draw(/* ... */);
shape = new Rectangle();
shape.draw(/* ... */);
Again, just an example.
If you're trying to take advantage of polymorphic behavior, you need to ensure that the methods visible to outside classes (that need polymorphism) have the same signature. That means they need to have the same name, number and order of parameters, as well as the parameter types.
In your case, you might do better to have a generic draw() method, and rely on the subclasses (Rectangle, Ellipse) to implement the draw() method as what you had been thinking of as "drawEllipse" and "drawRectangle".
I'm trying to implement a camera for a 2D game that I'm making... The goal will to have the cam keep the player in the center and the sprites relative to the camera.
To get the hang of normalocity's post, I tried starting off simple by making a Camera Test project, where I'd simulate a camera by drawing a sprite to a JPanel, and moving a "camera" object (which is the JPanel) around and setting the sprite's x,y relative to that.
The Camera, as I said, is the JPanel... and I've added a "world", which is a class with an x,y of 0,0, and w=1000, h=1000. I've included the sprite's location relative to the world as well as the camera. When I move the camera up, the sprite moves down and the player stays in the middle as expected..
But if I keep pressing down, the sprite seems to keep drawing over itself.
My questions are:
Am I on the right track in implementing a camera given the code below?
Why does the sprite start to draw over itself there? It should just disappear off the viewPort/JPanel
Thanks!
Now with PaintComponent(g) added, my JPanel bg color of gray now slides off. Is this supposed to happen?
EDIT: SSCCE of my program:
Main Class:
import java.awt.Dimension;
import java.awt.Toolkit;
import javax.swing.JFrame;
#SuppressWarnings("serial")
public class MainSSCCE extends JFrame {
static MainSSCCE runMe;
public MainSSCCE() {
JFrame f = new JFrame("Camera Test");
CameraSSCCE cam = new CameraSSCCE(0, 0, 500, 500);
f.add(cam);
f.setSize(cam.getWidth(), cam.getHeight());
f.setVisible(true);
f.setResizable(false);
f.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
Dimension screensize = Toolkit.getDefaultToolkit().getScreenSize();
f.setLocation( (screensize.width - f.getWidth())/2,
(screensize.height - f.getHeight())/2-100 );
}
public static void main(String[] args) {
runMe = new MainSSCCE();
}
}
Camera Class:
import java.awt.Color;
import java.awt.Graphics;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import javax.swing.JPanel;
//Camera is the JPanel that will draw all objects... each object location will be in relation to the World
public class CameraSSCCE extends JPanel implements KeyListener {
//add world to camera...
private static final long serialVersionUID = 1L;
private int camX, camY, camH, camW;
private SpriteSSCCE sprite;
private PlayerSSCCE player;
private WorldSSCCE world;
public CameraSSCCE(int x, int y, int w, int h) {
camX = x;
camY = y;
camW = w;
camH = h;
sprite = new SpriteSSCCE(this, 300, 300, 20, 20);
player = new PlayerSSCCE(this, camW/2, camH/2, 25, 40);
world = new WorldSSCCE(this, 0, 0, 1000, 1000);
addKeyListener(this);
setFocusable(true);
}
public int getWidth() {
return camW;
}
public int getHeight() {
return camH;
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
//cam is 500 x 500
g.setColor(Color.gray);
g.fillRect(camX, camY, camW, camH);
//draw sprite at JPanel location if in camera sight
if (((sprite.getX()-camX) >= camX) && ((sprite.getX()-camX) <= (camX+camW)) && ((sprite.getY()-camY) >= camY) && ((sprite.getY()-camY) <= (camY+camH))) {
g.setColor(Color.green);
g.fillRect(sprite.getX()-camX, sprite.getY()-camY, 20, 20);
//Cam Sprite Location
g.setColor(Color.white);
g.drawString("Camera Sprite Location: (" + (sprite.getX()-camX) + ", " + (sprite.getY()-camY) + ")", sprite.getX()-camX, sprite.getY()-camY);
}
//Player location (center of Camera... Camera follows player)
g.setColor(Color.cyan);
g.fillRect(player.getX()-player.getWidth(), player.getY()-player.getWidth(), player.getWidth(), player.getHeight());
g.setColor(Color.white);
//World Sprite Location
g.drawString("World Sprite Location: (" + sprite.getX() + ", " + sprite.getY() + ")", sprite.getX(), sprite.getY());
//Cam Player Location
g.drawString("Cam Player Location: (" + (camW/2-player.getWidth()) + ", " + (camH/2-player.getHeight()) + ")", camW/2-player.getWidth(), camH/2-player.getHeight());
}
public void keyPressed(KeyEvent e) {
//move camera right in relation to World
if (e.getKeyCode() == KeyEvent.VK_RIGHT) {
camX+=5;
}
//move camera left in relation to World
if (e.getKeyCode() == KeyEvent.VK_LEFT) {
camX-=5;
}
//move camera up in relation to World
if (e.getKeyCode() == KeyEvent.VK_UP) {
camY-=5;
}
//move camera down in relation to World
if (e.getKeyCode() == KeyEvent.VK_DOWN) {
camY+=5;
}
repaint();
}
public void keyReleased(KeyEvent e) {}
public void keyTyped(KeyEvent e) {}
}
World Class:
public class WorldSSCCE {
private int x, y, w, h;
private CameraSSCCE camera;
public WorldSSCCE(CameraSSCCE cam, int x, int y, int w, int h) {
camera = cam;
this.x = x;
this.y = y;
this.w = w;
this.h = h;
}
public int getX() {
return this.x;
}
public int getY() {
return this.y;
}
public int getWidth() {
return this.w;
}
public int getHeight() {
return this.h;
}
}
Player Class:
import java.awt.Dimension;
public class PlayerSSCCE {
private int x, y, w, h;
private CameraSSCCE cam;
public PlayerSSCCE(CameraSSCCE cm, int x, int y, int w, int h) {
cam = cm;
this.x = x;
this.y = y;
this.w = w;
this.h = h;
}
public int getX() {
return this.x;
}
public int getY() {
return this.y;
}
public int getWidth() {
return this.w;
}
public int getHeight() {
return this.h;
}
public void setX(int val) {
this.x += val;
}
public void setY(int val) {
this.y += val;
}
}
Sprite Class:
import java.awt.Color;
import java.awt.Graphics;
public class SpriteSSCCE {
private int xLoc, yLoc, width, height;
private CameraSSCCE world;
public SpriteSSCCE(CameraSSCCE wld, int x, int y, int w, int h) {
xLoc = x;
yLoc = y;
width = w;
height = h;
world = wld;
}
public int getX() {
return xLoc;
}
public int getY() {
return yLoc;
}
public int getWidth() {
return width;
}
public int getHeight() {
return height;
}
public void paintComponent(Graphics g) {
g.setColor(Color.green);
g.fillRect(xLoc, yLoc, width, height);
}
}
1) You have not honored the paint chain by calling super.paintComponent(g) in paintComponent(..):
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
//do drawing here
}
As per Java docs:
protected void paintComponent(Graphics g)
Further, if you do not invoker super's implementation you must honor
the opaque property, that is if this component is opaque, you must
completely fill in the background in a non-opaque color. If you do not
honor the opaque property you will likely see visual artifacts.
2) Also notice the #Override annotation I added and the fact that I changed public modifier to protected as thats what the access level is defined as in the implementation class which we should keep unless for a specific reason.
3) Also Swing uses Keybindings have a read on How to Use Key Bindings
4) Also have a read on Concurrency in Swing specifically on The Event Dispatch Thread which dictates all swing components be created on EDT via SwingUtillities.invokeXXX(..) block:
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
//create and manipulate swing components here
}
});
5) You extend the JFrame class and create an instance, this is not what you want rather remove the extends JFrame from the class declaration:
public class MainSSCCE extends JFrame { //<-- Remove extends JFrame
public MainSSCCE() {
JFrame f = new JFrame("Camera Test");//<-- instance is created here
}
}
Your world is a virtual area larger than the screen (or your jpanel for what matters). All objects' positions are relative to the world. Let's call them absolute coordinates.
Your camera is a small rectangular portion of the world (your panel). By moving it you see different world portions. If you could move the camera like in the post you link to, then at some point you would not be able to see neither the player nor the other sprite.
Since your goal is to keep the player centered on the screen what does this mean for our world? This means that the player and the camera are moving together in relation to the world.
Given the above it does not make sense to draw a camera sprite as in your first screenshot. The camera sprite should be either invisible or it should be drawn in the same position with the player sprite. Nor it makes sense to change the camera's absolute coordinates without changing the player's. Those two are moving together. (take this into account in your keyPressed() methods)
Now when you are drawing, you are drawing from the camera's point of view (or in other words in the camera's coordinate system). From that point of view, the camera always see a rectangle of (0, 0, cameraWidth, cameraHeight). That's what you should use when clearing the area with gray color. This will fix your moving background issue. Since camera and player always have the same absolute coordinates the player will always be in the same place (this is what we want). The rest of the sprites will be seen relative to camera.
For each one of them you translate them in the camera's coordinate system when you do (sprite.x - cam.x) and (sprite.y - cam.y). Since they are translated, you only need to check if they are inside the camera's rectangle (0, 0, cameraWidth, cameraHeight). If they are you go ahead and draw them. If not ignore them.
I hope that helps
Note: cameraWidth, cameraHeight are your jpanel's dimensions
i'm trying to write a jigsaw puzzle application where an image is cut in pieces, scrambled, and the user have to rearrange them with drag&drop to reassemble the original image. (something like this: http://www.jigzone.com/puzzles/74055D549FF0?z=5).
i have to write this in java with Graphics2d.
so, at first i'm trying to make some kind of component which can show a part of the image (a rectangle for now), and can be dragged with mouse.
the code below works well when there is only one one such component. the problem is, when i add the second component, the first one is no longer visible.
i'm really stuck here. i have a feeling i'm missing something really basic. or maybe i'm on a wrong way. any help will be greatly appreciated.
edit: i changed a bit the code according to suggestions, however, still not working as expected.
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.MouseInputAdapter;
public class GraphicDragAndDrop extends JPanel {
Rectangle rect;
Image img;
public GraphicDragAndDrop(String imgFile, int x0, int y0){
rect = new Rectangle(x0, y0, 150, 75);
img = new ImageIcon(imgFile).getImage();
}
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
g2d.setClip(rect);
int x = rect.x;
int y = rect.y;
g2d.drawImage(img, x, y, this);
}
public void setRect(int x, int y) {
rect.setLocation(x, y);
repaint();
}
public static void main(String[] args) {
// first piece
GraphicDragAndDrop piece1 = new GraphicDragAndDrop("a.png", 0, 0);
piece1.setRect(0, 0);
new GraphicDragController(piece1);
// second piece --> only this will be visible
GraphicDragAndDrop piece2 = new GraphicDragAndDrop("a.png", 200, 200);
//GraphicDragAndDrop piece2 = new GraphicDragAndDrop("b.png", 200, 200); // does'n work either
piece2.setRect(150, 150);
new GraphicDragController(piece2);
JFrame f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.add(piece1);
f.add(piece2);
f.setSize(500,500);
f.setLocation(300,100);
f.setVisible(true);
}
}
class GraphicDragController extends MouseInputAdapter {
GraphicDragAndDrop component;
Point offset = new Point();
boolean dragging = false;
public GraphicDragController(GraphicDragAndDrop gdad) {
component = gdad;
component.addMouseListener(this);
component.addMouseMotionListener(this);
}
public void mousePressed(MouseEvent e) {
Point p = e.getPoint();
Rectangle r = component.rect;
if(r.contains(p)) {
offset.x = p.x - r.x;
offset.y = p.y - r.y;
dragging = true;
}
}
public void mouseReleased(MouseEvent e) {
dragging = false;
}
public void mouseDragged(MouseEvent e) {
if(dragging) {
int x = e.getX() - offset.x;
int y = e.getY() - offset.y;
component.setRect(x, y);
}
}
}
Your code above is written to draw only one image:
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
g2d.setClip(rect);
int x = rect.x;
int y = rect.y;
// here
g2d.drawImage(new ImageIcon("a.png").getImage(), x, y, this);
}
If you need to draw more than one image, then consider creating a collection of images and iterating through the collection in paintComponent using a for loop:
also, never read in the image from within paintComponent since this method should be lean, mean and fast, and should concern itself with painting only. Also, there's no need to read the image in each time your program has to draw it as that's very inefficient and will slow the program unnecessarily. Instead read the image in once in the constructor or a similar init method.
For example,
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
for (Image img: myImageCollection) {
g2d.drawImage(img, 0, 0, this);
}
}
Edit
You state:
also, my plan was to have more objects of GraphicDragAndDrop class, each of them showing a different piece of the original image. is my approach wrong?
You could use components, but I have a feeling that it would be easy to drag images. I think it would be easier to rotate them for instance if you want your program to have this functionality. If not, though then sure use a component, but if you go this route, I would recommend using a JLabel and simply setting its ImageIcon rather than dragging JPanels.