Java-Gaming.org Hi !
Featured games (83)
games approved by the League of Dukes
Games in Showcase (512)
Games in Android Showcase (119)
games submitted by our members
Games in WIP (576)
games currently in development
News: Read the Java Gaming Resources, or peek at the official Java tutorials
 
   Home   Help   Search   Login   Register   
  Show Posts
Pages: [1]
1  Game Development / Newbie & Debugging Questions / Re: Coordinates in a tiled world on: 2010-06-14 07:54:16
Ok,
Makes sense.

Should the position of the objects be based on their center or top-left corner?
Will either affect the ability to have objects spanning several tiles?

Thanks!
2  Game Development / Newbie & Debugging Questions / Coordinates in a tiled world on: 2010-06-11 08:02:35
Hello,

I've tried out a couple of coordinate systems in a tiled world.
So far I'm not convinced that any of them is really what I'm looking for.

I'm working with a pacman clone (The level is tiled, but entities should be able to move freely).
The level is tiled from a 2d array. Each tile is 16x16.

My first attempt was just to use x:y coords for both objects and tiles.
(position and index in the tile-map). This resulted in pacman jumping from tile to tile
and not moving smoothly... clearly not what I was looking for.

My second attempt was to add delta coords for objects. This allowed me to
move the objects smoothly, but I feel using a delta is error prone. And should the major coord
be changed at the start/end/middle of a move?

Now I'm thinking about using different coordinates for objects and scenery.
And add a translation method. Does this sound like a good idea?

I would also like to have objects with larger than 1x1 tile sizes in the future.
Does any of the proposed coordinate systems limit this?

Thanks!
3  Games Center / Archived Projects / Re: FlipIt - Othello/Reversi online play between Android and Applet on: 2009-09-29 10:27:54
Excellent!
I'm really impressed if you consider this a "hack" Smiley

Will the source for this be available on www.cokeandcode.com?

Does your Applet/AppEngine solution use any special API for Applets or is the client/server communication regular http?

I'm also wondering about the login.
Would an Applet be able to use the UserService to associate an Applet with a Google user account?

Regards,
//Phear
4  Games Center / WIP games, tools & toy projects / Re: Stickmen Wars on: 2009-08-18 13:17:03
Great Gameplay. 2D RTS with all AI controlled units. Excellent!!!
Randomly generated (?) maps also increase replay value. Me like Smiley

I like that the terrain affects what will be the best strategy (hills provide cover for archers/mortar and flat areas are good for infantry).

I would like to see a sniper (slow, long-range infantry (100% to hit?)) and a flamethrower (slow, short-range splash-damage). Burning units could start to run back and ignite other units close by.
5  Game Development / Performance Tuning / Re: Quadtree for moving objects on: 2009-07-22 11:57:58
Hi again,

I made a reference implementation of my Quadtree : FlatQuadtree. Basically a wrapper for a single HashSet.
The performance gain compared to my other Quadtrees doesn't seem to be as great as I had hoped. Sad

With FlatQuadtree: 150 Boids @ 60hz
With either Static/DynamicQuadtree: 190 Boids @ 60hz

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
27  
28  
29  
30  
31  
32  
33  
34  
35  
36  
37  
38  
39  
40  
41  
42  
43  
44  
45  
46  
47  
48  
49  
50  
51  
52  
53  
54  
55  
56  
57  
58  
59  
60  
61  
62  
63  
64  
65  
66  
67  
68  
69  
70  
71  
72  
73  
74  
75  
76  
77  
78  
79  
80  
81  
82  
83  
import java.util.Collection;
import java.util.HashSet;

public class FlatQuadtree implements Quadtree {
   private final Collection<Entity> boids = new HashSet<Entity>();

   @Override
   public void add(Entity entity) {
      boids.add(entity);
   }

   @Override
   public Collection<Entity> get() {
      final Collection<Entity> result = new HashSet<Entity>();
      get(result);
      return result;
   }

   @Override
   public void get(Collection<Entity> result) {
      result.addAll(boids);
   }

   @Override
   public Collection<Entity> get(BoundingSquare bs) {
      final Collection<Entity> result = new HashSet<Entity>();
      get(bs, result);
      return result;
   }

   @Override
   public void get(BoundingSquare bs, Collection<Entity> result) {
      for (Entity boid : boids) {
         if (bs.contains(boid.getPos())) {
            result.add(boid);
         }
      }
   }

   @Override
   public Collection<Entity> get(BoundingCircle bc) {
      final Collection<Entity> result = new HashSet<Entity>();
      get(bc, result);
      return result;
   }

   @Override
   public void get(BoundingCircle bc, Collection<Entity> result) {
      for (Entity boid : boids) {
         if (bc.contains(boid.getPos())) {
            result.add(boid);
         }
      }
   }

   @Override
   public void lock(boolean lock) {}

   @Override
   public int maxLevel() {
      return 1;
   }

   @Override
   public void moveDoit(Entity entity, Point pos) {
      throw new RuntimeException();
   }

   @Override
   public boolean moveTest(Entity entity, Point pos) {
      return true;
   }

   @Override
   public void remove(Entity entity) {
      boids.remove(entity);
   }

   @Override
   public int size() {
      return boids.size();
   }
}



I've also got a new question: Should it be possible for an object to be located in more than one BB?
Right now all objects are limited to one BB. The add method return after the 1st successful add operation.
But if I want to use Quadtrees for colission detection I assume that their location in the Quadtree should be based on their position *and* size and not just their position. Right?
6  Game Development / Performance Tuning / Re: Quadtree for moving objects on: 2009-07-16 16:51:07
The reason your remove is slow, is that you remove from a Vector.

First you have to search the vector (on average you have to check 50% of the items), and them you have to remove the item (on average you have to shift 50% of the remaining items).

If you replace the Vector by a HashSet, you'd get far better results.

...adding will be a tad slower though...

Whoa!

After changing Vector to HashSet the add time was reduced from ~17sec to ~200ms
Remove time is reduced from ~13sec to ~4sec
Search times are approximately doubled

Odd... Smiley
7  Game Development / Performance Tuning / Re: Quadtree for moving objects on: 2009-07-16 16:35:15
WRT: Being a 2D array that was in reference to "even distributed" not the code, I should have been more clear.

WRT: Being overcomplex. Examples: Nodes is taking on two rolls, that of an interior node and a leaf.  Since your only storing data in leaves the code and data size can be reduced by breaking in two.  'splitSize', 'joinSize' and 'bb' could be replaced by a reference to the tree and a chosen coordinate. (The extent is implicit from the overall size and the depth).  This should speed up construction time.

splitSize and joinSize are now moved to the Quadtree class. Still available to the inner class. Thanks! Smiley
I will try to change the BB class into a set of static methods that will work with the "level" and one point. Not sure how readable the code will be however...
8  Game Development / Performance Tuning / Re: Quadtree for moving objects on: 2009-07-16 13:30:07
That is exactly how a quadtree normally works - if you rebuild the quadtree from scratch every frame based on the data you're attempting to classify, which is all you have to do. So do that. You'll then find that the quadtree nodes end up being quite a lot of garbage, so you might want to implement a quadtree node factory that uses a pool.

Cas Smiley

This sounds like something that will help during move (remove/add).
Now I get odd profiling results. One of my tests (add 64k of Entities, remove them) generally executes in 30 sec. However every once in a while it takes almost a minute.
9  Game Development / Performance Tuning / Re: Quadtree for moving objects on: 2009-07-16 13:18:59
Ah! But that's the whole point.  Otherwise you just have a 2D array with an overly complex interface (and memory requirements).  Surely there is some existing Java code you could look at.

How did my "on-demand tree" become a 2D array?
Exactly why is there a point in having n levels of populated Quadtree if the parent can handle the request?

Please try to be more specific about the parts that are "way over complex with lots of redundent data".
I've tried to write this as straightforward as possible so that others can read it and help me improve it.
10  Game Development / Performance Tuning / Re: Quadtree for moving objects on: 2009-07-16 12:20:40
Not sure what you mean by "evenly distributed", but the whole point of a quadtree is to localize queries.  Skimming your code, it looks way over complex with lots of redundent data.

I tried to say that if most of my Boids fly around in the top right corner. It would be better to have a high resolution in that corner and a low resolution in the other corners. No need to generate the nodes where there are no Boids.
11  Game Development / Performance Tuning / Re: Quadtree for moving objects on: 2009-07-16 11:53:53
Because my Boids fly in flocks and doesn't spread out evenly I think an evenly distributed Quadtree would be a waste.

Enter the DynamicQuadtree:

My DynamicQuadtree is in many ways identical to the StaticQuadtree. However it uses lazy-load to generate the lower layers. A new layer is generated when more then a specified number of Entities has been added to a Node.

Many things improved directly:
+ Inital creation of a Quadtree is now very quick
+ Searching in an empty tree is now much quicker
However:
- Add and remove operations are basically unaffected Sad
64k Entities take ~20 sec to add and ~10sek to remove

Can anyone direct me in the way to improve this?
Are there more parameters than bucket size and tree depth?

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
27  
28  
29  
30  
31  
32  
33  
34  
35  
36  
37  
38  
39  
40  
41  
42  
43  
44  
45  
46  
47  
48  
49  
50  
51  
52  
53  
54  
55  
56  
57  
58  
59  
60  
61  
62  
63  
64  
65  
66  
67  
68  
69  
70  
71  
72  
73  
74  
75  
76  
77  
78  
79  
80  
81  
82  
83  
84  
85  
86  
87  
88  
89  
90  
91  
92  
93  
94  
95  
96  
97  
98  
99  
100  
101  
102  
103  
104  
105  
106  
107  
108  
109  
110  
111  
112  
113  
114  
115  
116  
117  
118  
119  
120  
121  
122  
123  
124  
125  
126  
127  
128  
129  
130  
131  
132  
133  
134  
135  
136  
137  
138  
139  
140  
141  
142  
143  
144  
145  
146  
147  
148  
149  
150  
151  
152  
153  
154  
155  
156  
157  
158  
159  
160  
161  
162  
163  
164  
165  
166  
167  
168  
169  
import java.util.Collection;
import java.util.Vector;

public class DynamicQuadtree implements Quadtree {
   private final static int DEFAULT_SPLIT_SIZE = 5;
   private final static int DEFAULT_LEVELS = 5;
   private final Collection<Entity> entities = new Vector<Entity>();
   private final Node head;

   public DynamicQuadtree() {
      this(DEFAULT_SPLIT_SIZE, DEFAULT_LEVELS);
   }
   public DynamicQuadtree(int splitSize, int levels) {
      if (splitSize < 1) throw new IllegalArgumentException();
      if (levels < 0) throw new IllegalArgumentException();
      this.head = new Node(splitSize, levels, new BoundingBox(-256, -256, 256, 256));
   }

   @Override
   public synchronized int size() {
      return head.size();
   }
   @Override
   public synchronized Collection<Entity> get() {
      return new Vector<Entity>(entities);
   }
   @Override
   public synchronized void get(BoundingBox bb, Collection<Entity> result) {
      head.get(bb, result);
   }
   @Override
   public synchronized void add(Entity entity) {
      if (entities.contains(entity)) throw new RuntimeException();

      entities.add(entity);
      head.add(entity);
   }
   @Override
   public synchronized void remove(Entity entity) {
      if (!entities.contains(entity)) throw new RuntimeException();

      entities.remove(entity);
      head.remove(entity);
   }

   @Override
   public String toString() {
      return "[QT "+ head +"]";
   }

   private final class Node {
      private final int splitSize;
      private final int joinSize;
      private final int level;
      private final BoundingBox bb;
      private Collection<Entity> entities;
      private Node minmin, minmax, maxmin, maxmax;

      public Node(int splitSize, int level, BoundingBox bb) {
         if (level < 0) throw new IllegalArgumentException();

         this.splitSize = splitSize;
         this.joinSize = splitSize*2;
         this.level = level;
         this.bb = bb;
         this.entities = new Vector<Entity>();
      }

      public int size() {
         if (entities != null) {
            return entities.size();
         } else { // is split
            int sum = 0;
            sum += minmin.size();
            sum += minmax.size();
            sum += maxmin.size();
            sum += maxmax.size();
            return sum;
         }
      }
      public void get(Collection<Entity> result) {
         if (entities != null) {
            result.addAll(entities);
         } else { // is split
            minmin.get(result);
            minmax.get(result);
            maxmin.get(result);
            maxmax.get(result);
         }
      }
      public void get(BoundingBox bb, Collection<Entity> result) {
         if (!this.bb.intersects(bb)) throw new RuntimeException();

         if (entities != null) {
            for (Entity entity : entities) {
               if (bb.contains(entity.pos)) result.add(entity);
            }
         } else { // is split
            if (minmin.bb.intersects(bb)) minmin.get(bb, result);
            if (minmax.bb.intersects(bb)) minmax.get(bb, result);
            if (maxmin.bb.intersects(bb)) maxmin.get(bb, result);
            if (maxmax.bb.intersects(bb)) maxmax.get(bb, result);
         }
      }
      public void add(Entity entity) {
         if (!this.bb.contains(entity.pos)) throw new RuntimeException();

         if (entities != null) {
            entities.add(entity);

            if (entities.size() >= splitSize && level > 0) { // split
               final Collection<Entity> tmpEntities = new Vector<Entity>(entities);

               entities = null;
               final double dx = (bb.maxX - bb.minX)/2;
               final double dy = (bb.maxY - bb.minY)/2;
               minmin = new Node(splitSize, level-1, new BoundingBox(bb.minX, bb.minY, bb.maxX-dx, bb.maxY-dy));
               minmax = new Node(splitSize, level-1, new BoundingBox(bb.minX, bb.minY+dy, bb.maxX-dx, bb.maxY));
               maxmin = new Node(splitSize, level-1, new BoundingBox(bb.minX+dx, bb.minY, bb.maxX, bb.maxY-dy));
               maxmax = new Node(splitSize, level-1, new BoundingBox(bb.minX+dx, bb.minY+dy, bb.maxX, bb.maxY));

               for (Entity tmpEntity : tmpEntities) {
                  add(tmpEntity);
               }
            }
         } else { // is split
            if (minmin.bb.contains(entity.pos)) minmin.add(entity);
            if (minmax.bb.contains(entity.pos)) minmax.add(entity);
            if (maxmin.bb.contains(entity.pos)) maxmin.add(entity);
            if (maxmax.bb.contains(entity.pos)) maxmax.add(entity);
         }
      }
      public void remove(Entity entity) {
         if (!this.bb.contains(entity.pos)) throw new RuntimeException();

         if (entities != null) {
            entities.remove(entity);
         } else { // is split
            if (minmin.bb.contains(entity.pos)) minmin.remove(entity);
            if (minmax.bb.contains(entity.pos)) minmax.remove(entity);
            if (maxmin.bb.contains(entity.pos)) maxmin.remove(entity);
            if (maxmax.bb.contains(entity.pos)) maxmax.remove(entity);

            int sum = 0;
            sum += minmin.size();
            sum += minmax.size();
            sum += maxmin.size();
            sum += maxmax.size();
            if (sum < joinSize) {
               entities = new Vector<Entity>();
               minmin.get(entities); minmin = null;
               minmax.get(entities); minmax = null;
               maxmin.get(entities); maxmin = null;
               maxmax.get(entities); maxmax = null;
            }
         }
      }

      public String toString() {
         StringBuffer sb = new StringBuffer();
         if (entities != null) sb.append(entities);
         if (minmin != null) sb.append(minmin);
         if (minmax != null) sb.append(minmax);
         if (maxmin != null) sb.append(maxmin);
         if (maxmax != null) sb.append(maxmax);
         return "[Node "+ sb.toString() +"]";
      }
   }
}
12  Game Development / Performance Tuning / Quadtree for moving objects on: 2009-07-16 09:11:57
This is my first Quadtree implementation and I'm not too impressed with it Sad

I started out because I wanted to speed up my Boids application (http://www.red3d.com/cwr/boids/).
1) Boids move around
2) Boids adapt their movement based on their closest neigbours

Without any form of spatial organisation I had a O(n^2) complexity for each update

Enter the StaticQuadtree...

My StaticQuadtree generates all lower levels of the tree on instantiation.
A StaticQuadtree with 1 level is basically the same as my original Vector.
After some performance tests I noticed that generating more than 5 levels starts to decrease performance. Especially when removing Boids from the Quadtree when they move and when searching in an area that contains several sub levels.

Is that a normal number of levels for a Quadtree, or can this code be improved by multitudes after a few simple optimizations? Smiley

Also, am I correct in asuming that I have to remove and re-add all boids during update?
Wouldn't it be easier (quicker even) to regenerate the Quadtree before each update?

My first implementation:
1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
27  
28  
29  
30  
31  
32  
33  
34  
35  
36  
37  
38  
39  
40  
41  
42  
43  
44  
45  
46  
47  
48  
49  
50  
51  
52  
53  
54  
55  
56  
57  
58  
59  
60  
61  
62  
63  
64  
65  
66  
67  
68  
69  
70  
71  
72  
73  
74  
75  
76  
77  
78  
79  
80  
81  
82  
83  
84  
85  
86  
87  
88  
89  
90  
91  
92  
93  
94  
95  
96  
97  
98  
99  
100  
101  
102  
103  
104  
105  
106  
107  
108  
109  
110  
111  
112  
113  
114  
115  
116  
117  
118  
119  
120  
121  
122  
123  
124  
125  
126  
127  
128  
129  
130  
131  
132  
133  
134  
135  
136  
137  
138  
139  
140  
141  
142  
143  
144  
145  
146  
147  
148  
149  
150  
151  
152  
153  
154  
155  
156  
157  
158  
159  
160  
161  
162  
163  
164  
165  
166  
167  
168  
169  
170  
171  
172  
173  
174  
175  
176  
177  
178  
179  
180  
181  
182  
183  
184  
185  
186  
187  
188  
189  
190  
191  
192  
193  
194  
195  
196  
197  
198  
199  
200  
201  
import java.util.Collection;
import java.util.Vector;

public class StaticQuadtree implements Quadtree {
   private final static int DEFAULT_LEVELS = 5;
   private final Collection<Entity> entities = new Vector<Entity>();
   private final Node head;

   public StaticQuadtree() {
      this(DEFAULT_LEVELS);
   }
   public StaticQuadtree(int levels) {
      if (levels < 0) throw new IllegalArgumentException();
      head = new Node(levels, new BoundingBox(-256, -256, 256, 256));
   }

   @Override
   public synchronized int size() {
      return entities.size();
   }
   @Override
   public synchronized Collection<Entity> get() {
      return new Vector<Entity>(entities);
   }
   @Override
   public synchronized void get(BoundingBox bb, Collection<Entity> result) {
      head.get(bb, result);
   }
   @Override
   public synchronized void add(Entity entity) {
      if (entities.contains(entity)) throw new RuntimeException();

      entities.add(entity);
      head.add(entity);
   }
   @Override
   public synchronized void remove(Entity entity) {
      if (!entities.contains(entity)) throw new RuntimeException();

      entities.remove(entity);
      head.remove(entity);
   }

   @Override
   public String toString() {
      return "[QT "+ head +"]";
   }

   private final class Node {
      private final int level;
      private final BoundingBox bb;
      private final Collection<Entity> entities;
      private final Node minmin, minmax, maxmin, maxmax;

      public Node(final int level, BoundingBox bb) {
         if (level < 0) throw new IllegalArgumentException();
         this.level = level;
         this.bb = bb;

         if (level > 0) {
            entities = null;
            final double dx = (bb.maxX - bb.minX)/2;
            final double dy = (bb.maxY - bb.minY)/2;
            minmin = new Node(level-1, new BoundingBox(bb.minX,    bb.minY,    bb.maxX-dx, bb.maxY-dy));
            minmax = new Node(level-1, new BoundingBox(bb.minX,    bb.minY+dy, bb.maxX-dx, bb.maxY));
            maxmin = new Node(level-1, new BoundingBox(bb.minX+dx, bb.minY,    bb.maxX,    bb.maxY-dy));
            maxmax = new Node(level-1, new BoundingBox(bb.minX+dx, bb.minY+dy, bb.maxX,    bb.maxY));
         } else {
            entities = new Vector<Entity>();
            minmin = null;
            minmax = null;
            maxmin = null;
            maxmax = null;
         }
      }

      public synchronized int size() {
         if (level == 0) {
            return entities.size();
         }
         int sum = 0;
         sum += minmin.size();
         sum += minmax.size();
         sum += maxmin.size();
         sum += maxmax.size();
         return sum;
      }
      public synchronized void get(Collection<Entity> result) {
         if (level == 0) {
            result.addAll(entities);
         } else {
            minmin.get(result);
            minmax.get(result);
            maxmin.get(result);
            maxmax.get(result);
         }
      }
      public synchronized void get(BoundingBox bb, Collection<Entity> result) {
         if (!this.bb.intersects(bb)) throw new RuntimeException();

         if (level == 0) {
            for (Entity entity : entities) {
               if (bb.contains(entity.pos)) {
                  result.add(entity);
               }
            }
         } else {
            if (minmin.bb.intersects(bb)) minmin.get(bb, result);
            if (minmax.bb.intersects(bb)) minmax.get(bb, result);
            if (maxmin.bb.intersects(bb)) maxmin.get(bb, result);
            if (maxmax.bb.intersects(bb)) maxmax.get(bb, result);
         }
      }
      public synchronized void add(Entity entity) {
         if (!this.bb.contains(entity.pos)) throw new RuntimeException();

         if (level == 0) {
            entities.add(entity);
         } else {
            if (minmin.bb.contains(entity.pos)) minmin.add(entity);
            if (minmax.bb.contains(entity.pos)) minmax.add(entity);
            if (maxmin.bb.contains(entity.pos)) maxmin.add(entity);
            if (maxmax.bb.contains(entity.pos)) maxmax.add(entity);
         }
      }
      public synchronized void remove(Entity entity) {
         if (!this.bb.contains(entity.pos)) throw new RuntimeException();

         if (level == 0) {
            entities.remove(entity);
         } else {
            if (minmin.bb.contains(entity.pos)) minmin.remove(entity);
            if (minmax.bb.contains(entity.pos)) minmax.remove(entity);
            if (maxmin.bb.contains(entity.pos)) maxmin.remove(entity);
            if (maxmax.bb.contains(entity.pos)) maxmax.remove(entity);
         }
      }

      @Override
      public String toString() {
         StringBuffer sb = new StringBuffer();
         if (entities != null) sb.append(entities);
         if (minmin != null) sb.append(minmin);
         if (minmax != null) sb.append(minmax);
         if (maxmin != null) sb.append(maxmin);
         if (maxmax != null) sb.append(maxmax);
         return "[Node "+ sb.toString() +"]";
      }
   }
}

final class Entity {
   public Point pos;
   public Point vel;

   public Entity(double x, double y) {
      this.pos = new Point(x, y);
      this.vel = new Point(0, 0);
   }

   @Override
   public String toString() {
      return "[Entity "+ pos.x +":"+ pos.y +"]";
   }
}

final class Point {
   public final double x, y;

   public Point(double x, double y) {
      this.x = x;
      this.y = y;
   }
}

final class BoundingBox {
   public final double minX, minY;
   public final double maxX, maxY;

   public BoundingBox(double minX, double minY, double maxX, double maxY) {
      if (minX >= maxX) throw new IllegalArgumentException();
      if (minY >= maxY) throw new IllegalArgumentException();

      this.minX = minX;
      this.minY = minY;
      this.maxX = maxX;
      this.maxY = maxY;
   }

   public boolean contains(double x, double y) {
      return (minX < x && x <= maxX &&
            minY < y && y <= maxY);
   }
   public boolean contains(Point that) {
      return contains(that.x, that.y);
   }
   public boolean intersects(BoundingBox that) {
      return (minX < that.maxX && that.minX <= maxX &&
            minY < that.maxY && that.minY <= maxY);
   }
}


Stay tuned for my DynamicQuadtree update... Smiley
13  Game Development / Game Play & Game Design / Re: MVC on: 2009-06-18 10:50:36
Would this be a correct MVC design?
The Model is now stupid
The Controller is responsible for updating the Model
The Viewer is observing the Controller instead of the Model <-- is this correct MVC design?

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
27  
28  
29  
30  
31  
32  
33  
34  
35  
36  
37  
38  
39  
40  
41  
42  
43  
44  
45  
46  
47  
48  
49  
50  
51  
52  
53  
54  
55  
56  
57  
58  
59  
60  
61  
62  
63  
64  
65  
66  
67  
68  
69  
70  
71  
72  
73  
74  
75  
76  
77  
78  
79  
80  
81  
82  
83  
84  
85  
86  
87  
88  
89  
90  
91  
92  
93  
94  
95  
96  
97  
98  
99  
100  
101  
102  
103  
104  
105  
106  
107  
108  
109  
110  
111  
112  
113  
114  
115  
116  
117  
118  
119  
120  
121  
122  
123  
124  
125  
126  
127  
128  
129  
130  
131  
132  
133  
134  
135  
136  
137  
138  
139  
140  
141  
142  
143  
144  
145  
146  
147  
148  
149  
150  
151  
152  
153  
154  
155  
156  
157  
158  
159  
160  
161  
162  
163  
164  
165  
166  
167  
168  
169  
170  
171  
172  
173  
174  
175  
176  
177  
178  
179  
180  
181  
182  
183  
184  
185  
186  
187  
188  
189  
190  
191  
192  
193  
194  
195  
196  
197  
198  
199  
200  
201  
202  
203  
204  
205  
206  
207  
208  
209  
210  
211  
212  
213  
214  
215  
216  
217  
218  
219  
package rps;

import java.awt.BorderLayout;
import java.awt.Font;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Observable;
import java.util.Observer;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;

public class RPS2 {

   private enum Hand { Rock, Paper, Scissor }
   private enum Player { Attacker, Defender }

   /**
    * @param args
    */

   public static void main(String[] args) {
      try {
           UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
       }
       catch (UnsupportedLookAndFeelException e) {}
       catch (ClassNotFoundException e) {}
       catch (InstantiationException e) {}
       catch (IllegalAccessException e) {}

       new RPS2();
   }

   private RPS2() {
      Model model = new Model();
      Controller controller = new Controller(model);
      new View(controller, Player.Attacker);
      new View(controller, Player.Defender);
   }

   // Model
   private class Model {
      private int score;
      private Hand attacker;
      private Hand defender;

      public void setAttacker(Hand hand) {
         this.attacker = hand;
      }
      public void setDefender(Hand hand) {
         this.defender = hand;
      }

      @Override
      public String toString() { return "[Model]"; }
   }

   // Controller
   private class Controller extends Observable {
      private final Model model;

      public Controller(Model model) {
         this.model = model;
      }

      public void setHand(Player player, Hand hand) {
         if (player == Player.Attacker) {
            model.setAttacker(hand);
            setChanged();
         }
         if (player == Player.Defender) {
            model.setDefender(hand);
            setChanged();
         }
         testVictory();
      }


      // Test for Victory
      // Update score and reset attacker and defender
      private void testVictory() {
         if (model.attacker == null) return;
         if (model.defender == null) return;

         Player winner = null;
         if ((model.attacker == Hand.Rock    && model.defender == Hand.Scissor) ||
            (model.attacker == Hand.Paper   && model.defender == Hand.Rock)    ||
            (model.attacker == Hand.Scissor && model.defender == Hand.Paper)) {
            model.score++;
            winner = Player.Attacker;
         }
         if ((model.defender == Hand.Rock    && model.attacker == Hand.Scissor) ||
            (model.defender == Hand.Paper   && model.attacker == Hand.Rock)    ||
            (model.defender == Hand.Scissor && model.attacker == Hand.Paper)) {
            model.score--;
            winner = Player.Defender;
         }
         notifyObservers(winner);

         model.attacker = null;
         model.defender = null;
      }

      public int getScore(Player player) {
         if (player == Player.Attacker)
            return model.score;
         if (player == Player.Defender)
            return -model.score;
         return 0;
      }
      public Hand getHand(Player player) {
         if (player == Player.Attacker)
            return model.attacker;
         if (player == Player.Defender)
            return model.defender;
         return null;
      }

      @Override
      public String toString() { return "[Controller]"; }
   }

   // View
   private class View extends JFrame implements Observer {
      private static final long serialVersionUID = 1L;
      private static final String VICTORY = "Victory!";
      private static final String DEFEAT  = "Defeat!";
      private static final String DRAW    = "Draw!";
      private final Controller model;
      private final Player player;
      private final JLabel score = new Label();
      private final JLabel info  = new Label();

      public View(Controller controller, Player player) {
         super(player.toString());
         this.model = controller;
         this.player = player;
         controller.addObserver(this);

         updateScore();
         add(score, BorderLayout.NORTH);
         add(new Panel());
         add(info, BorderLayout.SOUTH);

         setDefaultCloseOperation(EXIT_ON_CLOSE);
         setResizable(false);
         pack();
         setLocationByPlatform(true);
         setVisible(true);
      }

      private void updateScore() {
         this.score.setText("Score: "+ model.getScore(player));
      }
      private void notifyWinner(Player player) {
         if (player == null) {
            info.setText(DRAW);
         } else if (player == this.player) {
            info.setText(VICTORY);
         } else {
            info.setText(DEFEAT);
         }
      }

      @Override
      public void update(Observable o, Object note) {
         System.out.println(this +" "+ o +" "+ note);
         updateScore();
         if (note == null || note instanceof Player) {
            notifyWinner((Player) note);
         }
      }

      @Override
      public String toString() { return "[View]"; }

      private class Panel extends JPanel {
         private static final long serialVersionUID = 1L;
         public Panel() {
            super(new GridLayout(1, 3));
            add(new Button(Hand.Rock));
            add(new Button(Hand.Paper));
            add(new Button(Hand.Scissor));
         }

         private class Button extends JButton implements ActionListener {
            private static final long serialVersionUID = 1L;
            private final Hand hand;

            public Button(Hand hand) {
               super(hand.toString());
               this.hand = hand;
               addActionListener(this);
            }

            @Override
            public void actionPerformed(ActionEvent ae) {
               View.this.model.setHand(player, hand);
            }
         }
      }

      private class Label extends JLabel {
         private static final long serialVersionUID = 1L;
         public Label(String message) {
            super(message);
            setHorizontalAlignment(JLabel.CENTER);
            setFont(getFont().deriveFont(Font.BOLD));
         }
         public Label() {
            this("...");
         }
      }
   }
}
14  Game Development / Game Play & Game Design / Re: MVC on: 2009-06-17 15:02:53
Here is an example of my apparently not so great MVC prowess...
I couldn't find a place for a Controller in this Rock-Paper-Scissor game.
Can anyone refactor the code below to use a Controller?

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
27  
28  
29  
30  
31  
32  
33  
34  
35  
36  
37  
38  
39  
40  
41  
42  
43  
44  
45  
46  
47  
48  
49  
50  
51  
52  
53  
54  
55  
56  
57  
58  
59  
60  
61  
62  
63  
64  
65  
66  
67  
68  
69  
70  
71  
72  
73  
74  
75  
76  
77  
78  
79  
80  
81  
82  
83  
84  
85  
86  
87  
88  
89  
90  
91  
92  
93  
94  
95  
96  
97  
98  
99  
100  
101  
102  
103  
104  
105  
106  
107  
108  
109  
110  
111  
112  
113  
114  
115  
116  
117  
118  
119  
120  
121  
122  
123  
124  
125  
126  
127  
128  
129  
130  
131  
132  
133  
134  
135  
136  
137  
138  
139  
140  
141  
142  
143  
144  
145  
146  
147  
148  
149  
150  
151  
152  
153  
154  
155  
156  
157  
158  
159  
160  
161  
162  
163  
164  
165  
166  
167  
168  
169  
170  
171  
172  
173  
174  
175  
176  
177  
178  
179  
180  
181  
182  
183  
package rps;

import java.awt.BorderLayout;
import java.awt.Font;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Observable;
import java.util.Observer;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;

public class RPS {

   private enum Hand { Rock, Paper, Scissor }
   private enum Player { Attacker, Defender }

   /**
    * @param args
    */

   public static void main(String[] args) {
      try {
           UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
       }
       catch (UnsupportedLookAndFeelException e) {}
       catch (ClassNotFoundException e) {}
       catch (InstantiationException e) {}
       catch (IllegalAccessException e) {}

       new RPS();
   }

   private RPS() {
      Model model = new Model();
      new View(model, Player.Attacker);
      new View(model, Player.Defender);
   }

   // Model
   private class Model extends Observable {
      private int score;
      private Hand attacker;
      private Hand defender;

      public void setAttacker(Hand hand) {
         this.attacker = hand;
         setChanged();

         testVictory();
      }
      public void setDefender(Hand hand) {
         this.defender = hand;
         setChanged();

         testVictory();
      }

      // Test for Victory
      // Update score and reset attacker and defender
      public void testVictory() {
         if (attacker == null) return;
         if (defender == null) return;

         if ((attacker == Hand.Rock && defender == Hand.Scissor) ||
            (attacker == Hand.Paper && defender == Hand.Rock) ||
            (attacker == Hand.Scissor && defender == Hand.Paper)) {
            score++;
            notifyObservers(Player.Attacker);
         } else
         if ((defender == Hand.Rock && attacker == Hand.Scissor) ||
            (defender == Hand.Paper && attacker == Hand.Rock) ||
            (defender == Hand.Scissor && attacker == Hand.Paper)) {
            score--;
            notifyObservers(Player.Defender);
         }
         else notifyObservers();

         attacker = null;
         defender = null;
      }

      @Override
      public String toString() { return "[Model]"; }
   }

   // View
   private class View extends JFrame implements Observer {
      private static final long serialVersionUID = 1L;
      private static final String VICTORY = "Victory!";
      private static final String DEFEAT  = "Defeat!";
      private static final String DRAW    = "Draw!";
      private final Model model;
      private final Player player;
      private final JLabel score = new Label();
      private final JLabel info  = new Label();

      public View(Model model, Player player) {
         super(player.toString());
         this.model = model;
         this.player = player;
         model.addObserver(this);

         updateScore();
         add(score, BorderLayout.NORTH);
         add(new Panel());
         add(info, BorderLayout.SOUTH);

         setDefaultCloseOperation(EXIT_ON_CLOSE);
         setResizable(false);
         pack();
         setLocationByPlatform(true);
         setVisible(true);
      }

      @Override
      public void update(Observable o, Object note) {
         updateScore();
         if (note == null || note instanceof Player) {
            notifyWinner((Player) note);
         }
      }
      private void updateScore() {
         final int score = (player == Player.Attacker) ? model.score : -model.score;
         this.score.setText("Score: "+ score);
      }
      private void notifyWinner(Player player) {
         if (player == null) {
            info.setText(DRAW);
         } else if (player == this.player) {
            info.setText(VICTORY);
         } else {
            info.setText(DEFEAT);
         }
      }

      private class Panel extends JPanel {
         private static final long serialVersionUID = 1L;
         public Panel() {
            super(new GridLayout(1, 3));
            add(new Button(Hand.Rock));
            add(new Button(Hand.Paper));
            add(new Button(Hand.Scissor));
         }

         private class Button extends JButton implements ActionListener {
            private static final long serialVersionUID = 1L;
            private final Hand hand;

            public Button(Hand hand) {
               super(hand.toString());
               this.hand = hand;
               addActionListener(this);
            }

            @Override
            public void actionPerformed(ActionEvent ae) {
               if (View.this.player == Player.Attacker) {
                  View.this.model.setAttacker(hand);
               } else {
                  View.this.model.setDefender(hand);
               }
            }
         }
      }

      private class Label extends JLabel {
         private static final long serialVersionUID = 1L;
         public Label(String message) {
            super(message);
            setHorizontalAlignment(JLabel.CENTER);
            setFont(getFont().deriveFont(Font.BOLD));
         }
         public Label() {
            this("...");
         }
      }
   }
}
15  Game Development / Game Play & Game Design / Re: MVC on: 2009-06-17 12:59:38
When discussing MVC, you really cannot leave out Controllers for the sake of simplicity.


The Model and View are separated by the Controller.

Now I'm lost. I thought Viewers registered as observers in the Model.
Should the Views register as observers in the Controller?
16  Game Development / Game Play & Game Design / Re: MVC on: 2009-06-17 09:56:39
Hi!

Sorry for my silly question but where are your controllers? Don't forget the C in MVC.

I left out the Controllers for simplicity. Smiley

I have other questions concerning the Controllers, but right now I'm focusing on the Model/Viewer part.
Right now there is one abstract Controller class (with an abstract execute() method) and one concrete class for every "verb" in the game logic (move, attach etc.).
17  Game Development / Game Play & Game Design / MVC on: 2009-06-17 08:32:40
Hello,

There are many topics regarding MVC. I like the following a lot.
http://www.java-gaming.org/topics/mvc-pattern/3685/view.html

However there are still a couple of issues that I feel uncertain of.
Should the Views register as observers to every Model entity or just the Model singleton?

- In a game like Ludo (static and limited number of pieces) I would assume that each piece should be observed by the viewers. Each move would then notify the Views.

- In an arcade game (Model entities are created and destroyed) I have registered the Views in the Model singleton only, and read all entities from the Model on notification. This works ok because all entities are updated in the Model every tick. In essence; all notifications are collated into a single notification.

- In a RTS (Views do not observe the entire Model, and different Views observe different parts of the Model) I'm a bit lost. I would prefer if the Views are notified when something "interesting" occurs in their View.
Pages: [1]
 

Add your game by posting it in the WIP section,
or publish it in Showcase.

The first screenshot will be displayed as a thumbnail.

Longarmx (49 views)
2014-10-17 03:59:02

Norakomi (39 views)
2014-10-16 15:22:06

Norakomi (31 views)
2014-10-16 15:20:20

lcass (35 views)
2014-10-15 16:18:58

TehJavaDev (66 views)
2014-10-14 00:39:48

TehJavaDev (65 views)
2014-10-14 00:35:47

TehJavaDev (55 views)
2014-10-14 00:32:37

BurntPizza (72 views)
2014-10-11 23:24:42

BurntPizza (43 views)
2014-10-11 23:10:45

BurntPizza (84 views)
2014-10-11 22:30:10
Understanding relations between setOrigin, setScale and setPosition in libGdx
by mbabuskov
2014-10-09 22:35:00

Definite guide to supporting multiple device resolutions on Android (2014)
by mbabuskov
2014-10-02 22:36:02

List of Learning Resources
by Longor1996
2014-08-16 10:40:00

List of Learning Resources
by SilverTiger
2014-08-05 19:33:27

Resources for WIP games
by CogWheelz
2014-08-01 16:20:17

Resources for WIP games
by CogWheelz
2014-08-01 16:19:50

List of Learning Resources
by SilverTiger
2014-07-31 16:29:50

List of Learning Resources
by SilverTiger
2014-07-31 16:26:06
java-gaming.org is not responsible for the content posted by its members, including references to external websites, and other references that may or may not have a relation with our primarily gaming and game production oriented community. inquiries and complaints can be sent via email to the info‑account of the company managing the website of java‑gaming.org
Powered by MySQL Powered by PHP Powered by SMF 1.1.18 | SMF © 2013, Simple Machines | Managed by Enhanced Four Valid XHTML 1.0! Valid CSS!