I want to double the size of an applet (including the graphics and components positioned on it).
Is there any easy way to accomplish this before I start changing every coordinate individually?
It has been a while since I wrote a Java applet, so I cannot tell you exactly which classes and interfaces.
But as a general principle you could do this with a decorator. Make a class that implements the same interface as your graphics context object*. Pass the actual graphics context to the constructor of your class. For each method on your class, call the same method on the graphic context object that it wraps. And set the magnification as a field of the class so you can make it 3x in future.
Then, when your code takes the graphics object, replace that with an instance of your custom class, which should wrap the graphics object.
If this is not possible, you may have to hack your original code. But take my advice - this time include a multiplication factor as a variable/field so that next time you can adjust the number in once place.
or, if there's no interface available, which looks to be the the Graphics class, extend it and be sure to override every method.
If the applet contents scale you just need to change the size of the applet in the embedding html page. This assumes that only relative or calculated coordinates and layout managers have been used. If elements are positioned at absolute positions, this will not work will. Then you will either have to touch the code or write a wrapper that scales the content, as Joe suggested.
Related
How can I type cast a shape (like a rounded rectangle) into a component?
Casting is simply changing a reference of an object to a different representation, not converting and object to an object of a different type.
Giving an example with some made up classes, you can cast a Shape to a Square, iff that Shape already actually is a Square:
Shape shape = new Square();
Square square = (Square) s; // this will be okay!
This is just exposing existing extra behaviour of the object associated with it's being a square, not converting it to a square. You can't do this, for example:
Shape shape = new Square();
Circle circle = (Circle) shape; // this will throw a ClassCastException
Because the shape is not actually a circle to begin with, and you would be potentially exposing behaviours (e.g. getPerimeter()) which would not actually make sense for the underlying object.
Similarly, in your case, a Shape is not a Component and it never will be, so you cannot cast it to be one. You cannot perform a cast to expose behaviour related to Components (adding listeners) because this behaviour is not supported by the Shape class. The shape class will not receive event messages from the interface even if it did make sense to add a listener to it, because it has not been added to the interface as a Component to begin with. Presumably you are painting the shapes onto a component manually, so you should already understand that they are not a part of the component hierarchy.
As you suggested, the best way to deal with this presuming you have already got to the point where the shapes are drawn inside a component, would be by manually checking that the x and y coordinates of the relevant MouseEvent are inside each Shape.
If you wanted to treat them as Components from the start, you would have to create Components for each shape, and add them to a Container using a LayoutManager, the same way you build the rest of the interface. This would involve creating a "custom Component" as you said, which rather than being the name a class or method which is ready to use, means to create your own sub-class of Component which could, perhaps, take a Shape argument in it's constructor. Here is an example I found for creating a custom button Component which is round, which with some modification should meet your needs.
You basically answered your own question for the most part in your last comment, this hopefully will help you understand how :).
Cheers, hope this helped.
From your further feedback I understand you just want to create a custom component.
Now the tricky question is: do you really want/need a custom component?
Normally you will only need extend the shape class and implement/override the contains() (, other methods that you require) and paint() methods where you draw what you want.
The listener can then be set within the container class (JFame,JPanel,..) that you use to place the shape and by using the shape.contains(point) you can handle events on it.
If you really need a component the major advantage is that they can handle events, interact with other components and also with layouts managers.
You can check this example on that.
Lets suppose we have a class Shape which has a method rotate(int velocity). This method makes a shape rotate with a speed of velocity(the parameter passed to rotate). This method has been called in a project, say at 100 places.
But now a new requirement comes, that the rotate functionality will also depend on the color of the shape, i.e. if the color is blue then the velocity should be decreased by 1, else no change should be made.
One solution to this problem would be to change the rotate(int velocity) method to rotate(int velocity, Color color), then add an if statement inside rotate method to check for the color, and make a change in 100 calls of rotate.
E.g.
shape.rotate(50, blue) ;
Inside the rotate method,
void rotate(int velocity, Color color) {
if(color == blue)
--velocity ;
}
Another solution would be to make color as an instance variable of the shape object, and then without adding a new argument to the rotate method, simply set the color before calling it, and squeeze the if check inside the rotate method.
E.g.
shape.setColor(blue) ;
shape.rotate(50) ;
Inside the rotate method,
void rotate(int velocity) {
if(this.color == blue)
--velocity ;
}
Yet another solution would be to overload the rotate method and create a new method named rotate(int velocity, Color color) and use it in the new calls. This would leave the existing code which uses rotate(int velocity) unchanged.
Which of these would be the best possible solution? Or, does there exist a better solution? If yes, then what could it be?
Regards
I'd say there are a couple of questions you need to ask yourself.
Do you care about the color outside of the rotate method? If yes, make it an instance variable; if no, pass it to the rotate method.
Are you likely to care about the color outside of the rotate method further down the line? If yes, make it an instance variable; if no, pass it to the rotate method.
Are you always going to care about the color when calling the rotate method? If yes, make it an argument (to force them to set the color when rotating the shape).
A good principle of OO is to co-locate related behavior and state. In this case the rotate behavior of shape depends on the colour state of shape, so it makes sense to co-locate both in the Shape class, c.q. create a field 'colour' and use it within the rotate method to customize the rotation behavior.
Apart from this design decision, you are really also asking about a refactoring decision: how do I handle the application code that depends on shape? My approach in cases like this is to think ahead: how many changes like these to the Shape class can we expect? If this is a rare change then you could just go ahead and change all the code locations that initialize the shape class so a colour is set. If shape changes more often, then you should be more rigorous and make your code less tightly coupled. A way to do that in this case is to create an abstract factory (or use the factory offered by a D.I. framework like Spring) so that the application code does not need to know the creation details of shape.
BTW your third option seems sub-optimal to me: part of the code is not made aware of the addition of the colour state to shape, and keeps calling the old 'deprecated' rotate method. This means that setting a shape's colour to blue will not universally affect the rotation behavior, but only in 'special cases'. This weakens the design and makes it harder for the developers after you to understand it.
I think the first option is is tedious to implement. What if you miss at one place, what if later u realize that you need rotate(single parameter) again.
The second option is irrelevant as many have already pointed out.
3rd I think is the best solution, as it will not break your code. You can have both the overloaded method, can use any of them as per requirement.
As for me, I see classic example of inheritance usage here.
class Shape {
public void rotate(int v) {}
}
class GreenShape extends Shape {
public void rotate(int v){
super.rotate(v + 10);
}
}
I have a custom UI drawn for my java application. Right now I draw the entire UI from scratch. I know for a fact some parts of the UI are static. I know I could save these static parts to an image, but will this yield a improvement in performance (Since even an image must be drawn every frame)?
Is it plausible to save a reference to the Graphics2D object after the UI has been drawn and assign that to the new graphics object every frame (starting from a point where all the static components are drawn)?
Thanks in advance,
Alan
You don't need to redraw everything in every frame. So if you have static parts of your UI (or even dynamic parts that you know haven't changed since last frame) then you simply don't need to repaint them.
In my code (Swing games and simulations mostly) I usually try to follow the following rules:
Pre-prepare static images (e.g. BufferedImage textures for UI elements)
Override the paintComponent() method for each UI element individually to do the painting
Only call the repaint() method of any given UI element if I know that something has changed
Call repaint() in a timer-based loop for animation, but only call it on the portion of the UI that is being animated (e.g. a nested JPanel)
This approach seems to work and perform pretty well (though I'd welcome comments if there are ways to improve it!!)
There are two main optimizations you can do here. The first is to make sure that when you cause your UI to be repainted, usually done by calling repaint, make sure you call the version of repaint where you specify a rectangle that has changed. Only make the rectangle big enough to encompass the parts that actually have changed, not the static parts. For this to be effective you also have to pay attention to the clipRect in the Graphics2D object you are passed in paint(). That is used by the system to tell you exactly what needs to be repainted, in the above case usually the rectangle that you passed to repaint. Don't paint anything that lies entirely outside that rectangle.
You can also get significant performance improvements by caching the static parts of your interface in an image. Writing an image is by far the fastest way of getting things onto a screen. My measurements indicate that small images are faster than even a few simple drawing primitives. However you need to make sure the image characteristics match the screen, by using createCompatibleImage().
Of course you may be using a lot of memory to get this speedup. I would recommend testing to see if you need to do image caching before implementing it.
if some parts of the screen is completely static, then never redraw that part. Don't do a full-screen/window clear, just clear the part of the screen/window that changes all the time.
This way, you don't unnecessarily redraw the static image.
EDIT: i just deleted the entire post and reformulated the question to be more generic.
I want to do a simple strategy game: map, units.
Map: one class. Units: another class, self drawn.
Simple questions:
How does an unit should redraw itself on the map.
A unit should be a JPanel or similar Swing component (just to be able to manage them as an entity with its own mousehandlers) or can be another thing, without neglecting the fact that it should be an autonomous object with its own action handlers and fields.
Is this map-units model correct of a simple game that would help me to learn in a fun way Java and OOP fundamentals.
Thats it!
There are 2 ways.
Either have a Map class which is the main JPanel, to maintain the collection of units, but keep the Unit class as non-Swing. When the Map's paint() method is called, ask each Unit to redraw itself by calling a method in each visible Unit. The Unit could be inherited from any base class, for example Rectangle, or some other data structure you use in the program. In this case, the Unit class handles painting and calculations, but the Map handles the clicks and events for each Unit. It could pass the message on to the Unit if needed; however, if the unit itself doesn't need to know about these things, this method works well. The Unit class is light and economical. You can also decide whether the Unit knows its position in the Map or not.
Or have each unit as a JComponent, and handle its own events separately. The disadvantage is that each instance of a Unit creates a pile of data for drawing it as well. So if you have hundreds of units, only a couple of which are drawn, this way is not efficient. The advantage is each unit can handle its own GUI events without translation etc. This assumes also a 1:1 mapping of actual units in the game and graphical units on the screen. Also it is trickier to implement certain event handlers in the Unit, if a Unit needs to know about what other Units are around!
A third and arguably better way is to have a purely data Unit class, containing the game information about the unit, as in (1), but which has a method to create the JComponent when needed. The JComponent could, for example, be an inner class in the Unit - it could then access the Unit data. This is great if a unit may need to be displayed in 2 places (e.g. on 2 screens or views).
--
Addendum to address comments
That is correct, in (1) the Map (main JPanel) implements the mouse handler, and asks each unit in turn if it is 'hit'. This allows you to do more complex things, such as have 2 units overlapping/on top of each other, and both could respond to the click.
Also, for example, they may not be rectangular, or may be drawn with an alpha channel (if the Units were JComponents, they will by default grab any mouse event over their whole rectangle, to themselves). If your units don't overlap and are in their own rectangles, then JComponent's own mouse handler is enough.
If you use approach (1), the Map can ask each unit whether it contains a clicked point, and the Map can then handle the 'selection' process. Remember, a unit on its own won't be able to work out what other units are selected - selection may involve deselecting another unit. In that case, the selection operation is really a Map operation, not a Unit operation, although it may change the appearance or function of the Unit as well.
If you use separate JComponents for the Units, you can override contains(Point) to determine if the mouse hits the item, but this won't let other Units also respond to the click. But each Unit could 'select itself' (set a flag used when drawing) then notify the Map (it will need to find it using getParent or by a preset property).
Key things you need to know before deciding on this design would be:
Will you ever need more than one 'Map' panel per game?
Will you ever need more Unit objects than are to be displayed?
Will you ever need to display a Unit more than once?
If the answers are yes, you should separate the 'Data' classes from the 'View' classes, as suggested in 3.
then: What does a Unit need to 'do' and what does it need to know about the Map and other Units in order to do this? e.g. moving is usually done by the Map class in this situation, as it depends on the Map and on other Units; drawing is best done by the Unit because there may be many unit subtypes with different data structures that may need to be drawn; Unit selection operations as you have pointed out lie somewhere in between. If you see how Swing implements this (e.g. ButtonGroup, ListSelectionModel), the 'selection' itself can be though of as a separate class.
I made a peg solitaire game a few years ago which was pretty much just a JPanel that extended MouseListener and MouseMotionListener. The pegs were primitive circles drawn in the paint method, but you could track where they landed by taking the cursor position and mathematically working out the square in which it landed. That lined up with an array that stored either a 1 or 0 for each square on the board depending if anything was there. You could also drag each piece by finding the current cursor position then calling repaint() in the mouseDragged method you get from implementing MouseMotionListener.
I'd probably suggest doing it that way to begin with. Create an array of whatever size and use that to track the units. Then each time you make a move just check that array and redraw your units in the paint method. If you're using mouse movement then you can get the current position of the unit on mouseDown then do what I mentioned before with mouseDragged, and then get your final location on mouseUp and also do your calculations in regards to legal moves etc in mouseUp.
In the paint method you'll just loop over the array that defines the map and if there is a unit at the current position on the array then do something like g.fillOval(x,y,x_dimension,y_dimension). That array could just have its elements set to 0 for no unit at the current position, or 1 for a team 1 unit at the current position, 2 for team 2 etc. The paint method will take those numbers and draw pieces accordingly (change color for each type or whatever).
I hope that makes a bit of sense.
I'm developing a fair sized hospital simulation game in java.
Right now, my pain method is starting to look a little big, and I need a way to split it up into different sections...
I have an idea, but I'm not sure if this is the best way.
It starts by painting the grass, then the hospital building, then any buildings, then people, then any building previews when building. The grass and hospital building will not change, so I only need to paint this once. The buildings themselves won't change very often, only when new ones are built.
I was thinking, use boolean values to determine which sections need repainting?
Ideal, id like to be able to split up the paint method, and then call each one when needed, but I'm unsure how to physically split it up.
I am still quite new to java, and learning on the go.
Thanks in advance.
Rel
Another idea is to create a super class or interface for all items that must be drawn on the screen. Lets cvall this class ScreenObject. You can then have a draw(Graphics2d g) method specified in the ScreenObject class. Next, each object that must be drawn implements the draw() method and is only concerned about drawing itself. You can even consider creating a variable that determines whether this draw method should be run at all.
In the main class that paints the screen you can have a reference to all ScreenObjects in an ArrayList and your paint() method will simply iterate over this calling draw() on each object.
I'm assuming from your description that your scene is split up into tiles. Keeping an array of booleans is a good way to keep track of which tiles need redrawn on the next update. A LinkedList might perform a little better in some situations. (I'm thinking of a Game of Life simulation where there are tons of tiles to redraw and you need to check each neighbor, so you may not need to go this route.)
Without seeing your code I can't give very specific advice on splitting up your paint method. I can tell you that in sprite animations, each sprite object typically has its own draw method that takes the main Graphics object (or more likely a buffer) as a parameter. Since the sprite should know its own image and location, it can then draw itself into the main image. Your paint method can then just loop through your list of sprites that need to be redrawn and call their draw method.
You might look to Killer Game Programming in Java for more detailed information.
Well I am not really an expert at programming but to split up my paint method Ive always just made a new method that takes a Graphics object and call that from paint, it has always helped me to keep my code organized but I have never had a big project like it sounds you are working on so it might not work for your situation.