views:

220

answers:

2

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:

  1. How does an unit should redraw itself on the map.
  2. 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.
  3. 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!

A: 

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.

Cameron
And if i want to have each unit self aware (for example, when clicked), how would you do that?
Gabriel A. Zorrilla
Instead of having an array of integers storing basic information on what is where you could have an array of unit objects. Just create a class called Unit or something, then inside that class have everything you want to store about each unit. You could even keep a similar integer array as well, just to check if something is a unit or a square of terrain. Depends on how you want to code it I guess.
Cameron
Inside each unit object you could have a boolean for whether it's active or not (which is turned on when you click that coordinate), then somewhere you could have a check for this, and then some sort of automated action could happen for example. Again it's up to you. Having an array of objects is quite powerful for this kind of stuff cos there is so much you can do with it.
Cameron
+1  A: 

There are 2 ways.

  1. 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.

  2. 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!

  3. 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.

Sanjay Manohar
Mmm, looking option 1, if i do not extend Unit to a JComponent i lose the mouse handler properties... or am i wrong?
Gabriel A. Zorrilla
And how, by clicking on map, a unit should know it has been selected? That kind of interaction is the one i dont clearly understand...
Gabriel A. Zorrilla
How do i pass an event from Map to Unit? Right now i'm thinking about a thread that checks (every 50 miliseconds) a clicked point on the map, then get that coorinates inside an array of Unit objects and if a unit actual position on the map matches (within certain ratio) the clicked point, it gets selected. Is there a better way? I'm just brainstorming. I'm beginning with threads right now.And BTW: all the logic regarding drawing the Map should be inside a big paint(Graphics g) method, right? For example, the array loop that gets in the thread mentioned before.
Gabriel A. Zorrilla
I think i could save creating a thread that loops inside the array by just doing that when clicking on the map.
Gabriel A. Zorrilla
The point of events is to obviate threads that check regularly for new occurrences. When your Map is created, it should call 'addMouseListener'. Java will automatically call your mouse listener with the coordinates of the click relative to the map, and you can then pass the events on to each unit in a for-loop, or check whether each unit contains the Point. If you use method (2) this isn't necessary, and each Unit can call 'addMouseListener' in its own constructor.
Sanjay Manohar