Java-Gaming.org    
Featured games (78)
games approved by the League of Dukes
Games in Showcase (429)
Games in Android Showcase (89)
games submitted by our members
Games in WIP (468)
games currently in development
News: Read the Java Gaming Resources, or peek at the official Java tutorials
 
    Home     Help   Search   Login   Register   
Pages: [1]
  ignore  |  Print  
  Need some help with a project I'm working on  (Read 2879 times)
0 Members and 1 Guest are viewing this topic.
Offline marinepower101

Junior Newbie





« Posted 2008-11-19 18:49:47 »

Hey guys, I've only recently started learning java (self teaching for around half a year) so that I could create this game I thought up.  It was basically thrown together, but is still the only java program I've made so far, so I would like to finish it up.  Currently, the code is approximately 5000 lines, but the more I look at it, the more it looks as if I could have written it better from the beginning.  (I looped through all my arrays at least 50 times every frame).

So my question is, "How would I go about optimizing my code"?  Right now, it is only one main applet class, with a class for every single unit, and a building class.  I would like to break up the huge paint method into different classes, but am unsure of how the best way to do this would be, as well as how to pass variables efficiently from one class to the next.

Also, I wanted to know if it is possible to make a png image a different color or rotate it, as there are multiple factions, and I wanted to make all their units and buildings their color.

Lastly, I was wondering how I could make effective unit movement.  As of now, all movement is calculated using distance, which requires me to loop through the whole map, except this does not work well.  There are some units which should not be able to cross water, or go across a pit, but using my method they just disappear and reappear somewhere else.

I have tried to solve all these problems independantly, but was unable to come up with a solution.  Here are some pics of the current build:


and the picture of the map editor:
Offline SimonH
« Reply #1 - Posted 2008-11-19 19:19:56 »

the only java program I've made so far... ...it looks as if I could have written it better from the beginning.
This is probably true! It may well be a good idea to start from scratch, based on what you've learned so far. Everybody's first go at code always ends up like spaghetti. Think MVC - Model, View, Controller and try to keep the code for these independent of eachother.

How would I go about optimizing my code?
Lol! The question at the back of every programmer's mind! Broadly: focus on those bits of code that get used a lot and try to make them quicker.

Lastly, I was wondering how I could make effective unit movement.
Read up on A* pathfinding.

Looks pretty good for a first attempt though!

People make games and games make people
Offline Jackal von ÖRF

Junior Member





« Reply #2 - Posted 2008-11-19 22:32:05 »

Quote
Currently, the code is approximately 5000 lines, but the more I look at it, the more it looks as if I could have written it better from the beginning.
Two options:
1. Refactor it.
2. Rewrite it.

Since you are just starting programming, rewriting a couple of programs might be good for exercise. But in the long run you must learn to refactor code.

Quote
So my question is, "How would I go about optimizing my code"?
"We should forget about small efficiencies, say about 97% of the time: premature optimization is the root of all evil."  - Donald Knuth

http://c2.com/cgi/wiki?MakeItWorkMakeItRightMakeItFast
http://c2.com/cgi/wiki?RulesOfOptimization

Quote
I would like to break up the huge paint method into different classes, but am unsure of how the best way to do this would be, as well as how to pass variables efficiently from one class to the next.
Cover the code with unit tests (it's easier when writing tests just before writing code - see TDD/BDD) and then refactor the code to make it better.

Some books about refactoring and code quality:
http://www.amazon.com/Refactoring-Improving-Existing-Addison-Wesley-Technology/dp/0201485672/
http://www.amazon.com/Clean-Code-Handbook-Software-Craftsmanship/dp/0132350882/

And to be able to do the above, you need to know about object-oriented design. You need to learn the principles of OO design:
http://www.lostechies.com/blogs/chad_myers/archive/2008/03/07/pablo-s-topic-of-the-month-march-solid-principles.aspx
http://butunclebob.com/ArticleS.UncleBob.PrinciplesOfOod
http://www.infoq.com/presentations/principles-agile-oo-design (fun video presentation Smiley)

Learning design patters is also useful:
http://en.wikipedia.org/wiki/Design_pattern_(computer_science)
http://www.amazon.com/Design-Patterns-Object-Oriented-Addison-Wesley-Professional/dp/0201633612/

Dependency injection is also a very useful pattern which I use all the time (and right now I prefer Guice for a DI framework), even though the above design pattern lists do not mention it:
http://en.wikipedia.org/wiki/Dependency_injection
http://martinfowler.com/articles/injection.html

Quote
Lastly, I was wondering how I could make effective unit movement.
http://en.wikipedia.org/wiki/A*

Games published by our own members! Check 'em out!
Legends of Yore - The Casual Retro Roguelike
Offline ewjordan

Junior Member





« Reply #3 - Posted 2008-11-20 01:21:37 »

Two options:
1. Refactor it.
2. Rewrite it.
IMO, if it's only 5000 lines and it's an early project, it screams for a rewrite from scratch, esp. since it will probably come out to less than half the length on a fresh rewrite.  I'd go as far as to say that even once your experienced, 5000 line prototypes should almost always be rewritten before going further, since you can create a big-picture design that reflects what worked and didn't work in the prototype.

Refactoring is what you do when you're in one of the following situations:
a) you got everything mostly right the first time through, and just need to clean house a bit
b) you've dug yourself too deep into the project to do the rewrite that it really needs

Don't end up in situation b) if you can help it.  No matter how great at refactoring you are, it's hard to "save" a project (by which I mean bring it to the point where it's fairly easy to extend it further) if it's gotten too crusty.  We've all been there more times than we'd like to admit, and most corporate code-bases are there in perpetuity, and it's a nasty mess that nobody likes to clean up after.

With small game projects you should be able to stay in category a) as long as you rewrite early and refactor often.
Offline Jackal von ÖRF

Junior Member





« Reply #4 - Posted 2008-11-20 10:24:14 »

The best time to clean up your mess is right after you have made the mess (or you notice a mess made by somebody else - if you need to maintain it, it's also your problem and responsibility to fix it). It's like keeping your living room clean - if every time you go through the room, you take something which is not in its right place and move it to where it belongs (take an empty glass to the kitchen etc.), you will never need a bulldozer to take down the room. The same is with code. If you refactor all the time (right when the code begins to smell - every couple of minutes right after some small changes), you will never need to rewrite everything (or if you think it like this - by refactoring you rewrite everything in so small steps that the system works all the time).

Clean Code is a good book which explains when to refactor and how to make the code better. I have almost finished reading that book now. Here is the table of contents. The first two chapters are also online:
http://www.informit.com/articles/article.aspx?p=1235624
http://tottinge.blogsome.com/meaningfulnames/

Quote
Don't end up in situation b) if you can help it.  No matter how great at refactoring you are, it's hard to "save" a project (by which I mean bring it to the point where it's fairly easy to extend it further) if it's gotten too crusty.
That's why good tests and refactoring are needed. This will change the way you think about code. Kent Beck's rules of Simple Design, which help in building well-designed software, are in order of priority:
1) Runs all the tests.
2) Has no duplicated logic. Be wary of hidden duplication like parallel class hierarchies.
3) States every intention important to the programmers.
4) Has the fewest possible classes and methods.
http://www.scribd.com/doc/12719/Extreme-Programming-Explained-Kent-Beck (page 49)
see also http://martinfowler.com/articles/designDead.html

If the system is already a rigid mess, such as most legacy systems, making the software again soft requires paying lots of technical debt. I have the book Working Effectively with Legacy Code which explains how to decouple bad code (I have not yet read the book). Here is a short introduction to the book:
http://www.objectmentor.com/resources/articles/WorkingEffectivelyWithLegacyCode.pdf

Offline Eli Delventhal

JGO Kernel


Medals: 42
Projects: 11


Game Engineer


« Reply #5 - Posted 2008-11-20 19:26:39 »

(I looped through all my arrays at least 50 times every frame)
This is not necessarily so bad. Many games will have O(n^2) efficiency when it comes to the main loop, where n is the number of entities in the world. This is typically as a result of a brute force method of determining proximity (each entity checks every other entity), which is certainly not optimized but in many games it's completely unnecessary to do something more efficient.

So my question is, "How would I go about optimizing my code"?
Sort of on the same line as what I said above, an old adage comes to mind. "If it ain't broke, don't fix it." Computers are fast enough these days that you can get away with a lot, especially with a game as simple as yours. That being said, one of the best things about learning to program is finding yourself in situations exactly like the one you're in now. You're looking back and a completed project and wondering how you could program something so badly.  Wink But that's a really great thing, because it means you've learned a lot and improved dramatically. And if you go back and rewrite it then that means you learn even more. So if you're going to rewrite it, don't do it for the sake of optimization, do it to learn. You could just as soon spend that time starting another project to get something else under your belt.

Right now, it is only one main applet class, with a class for every single unit, and a building class.  I would like to break up the huge paint method into different classes, but am unsure of how the best way to do this would be, as well as how to pass variables efficiently from one class to the next.
My paint method is usually very tiny, something like this:
1  
2  
3  
4  
5  
6  
7  
8  
9  
public void draw()
{
     background.draw(0,0,screenWidth,screenHeight);

     for (int i = 0; i < entities.size(); i++)
          entities.get(i).draw();

     player.draw();
}

So basically the background is always in the back and the player is always in the front. But really all I'm doing is referencing other draw methods, except for the background which we're assuming is an actual image. Each Entity should have their own draw() method where they are responsible for drawing themselves based on their own position, dimensions, current image, etc.

Also, I wanted to know if it is possible to make a png image a different color or rotate it, as there are multiple factions, and I wanted to make all their units and buildings their color.
There are a number of ways to do this. I'm not sure if this is the best way in Java2D, but you can just iterate over the raster of the BufferedImage you want to tint and then directly apply tinting values to each pixel. Messing with rasters is pretty fast so you shouldn't need to worry about speed as long as you're not tinting it every single timestep.

Lastly, I was wondering how I could make effective unit movement.  As of now, all movement is calculated using distance, which requires me to loop through the whole map, except this does not work well.  There are some units which should not be able to cross water, or go across a pit, but using my method they just disappear and reappear somewhere else.
You should try A*. Check out Kevin Glass's A* tutorial, it's pretty good: http://www.cokeandcode.com/pathfinding Also just do a search on the forums for A*. I might be misunderstanding what you're saying, by the way... I'm not exactly sure what "calculated using distance" means. It sounds like you're just setting them to their new possible position immediately, but then I don't see why you'd be iterating over the entire map.

Good luck!

See my work:
OTC Software
Offline marinepower101

Junior Newbie





« Reply #6 - Posted 2008-11-20 20:20:16 »

Thanks for the tips guys, but dang is that a lot of reading  Cheesy.  I know what I'll be doing this weekend.  Rewriting the entire applet seems like the best option at this point, but it just seems like such a waste of code.  That entity idea is great though, so I'll implement it the next time I work on a game.  Would it look something like:
1  
2  
3  
4  
5  
6  
public class Entity{
public Entity(){}
public void draw(Graphics g){
//g.draw stuff here;
}
}

and
1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
public class GameMap extends Entity{
int [][] map;
public GameMap(int x, int y){
map=new int[x][y];
}
public void draw(Graphics g){
for(int x=0;x<map.length;x++)
for(int y=0;y<map.length[0];y++)
g.drawImage(images.get(map[x][y]));
}
}


As for loading resources, would you guys just make some kind of resource class, that just loads every sound and image when its instantiated, and then pass that on to every other entity method, along with the Graphics?  The reason my original main applet class was so long was because of resource management.  One last thing, theres a class for every single unit, so that all their actions can be made unique from unit to unit.  Every unit also has a unique shooting sound, so I'm not sure what to do about that either.  Should I just do:
1  
2  
3  
4  
5  
6  
7  
8  
9  
if (unit instanceof CombatTank){
combatTankFire.stop();
combatTankFire.play();
}
else if (unit instanceof Medic){
medicHeal.stop();
medicHeal.play();
}
//... And so on, for all 20 units?
Offline phu004

JGO Coder


Medals: 4
Projects: 9
Exp: 10 years


NoSuchPersonException


« Reply #7 - Posted 2008-11-20 23:53:10 »

Sorry I have never played with sound in applet, but i have one suggestion for the drawing part

If your gameMap is fairly small, then just use a single big image for the map. (you can generate  the terrain at loading time)
If the gameMap is much bigger than the screen, then you should only draw the
part which is visible on the screen.

From your code it looks like you are drawing everything.
Offline Eli Delventhal

JGO Kernel


Medals: 42
Projects: 11


Game Engineer


« Reply #8 - Posted 2008-11-21 06:25:29 »

I made my own ImageManager class to handle my resource loading, and I've used it for practically every game I've ever made (since creating the class). It's simple, adaptable, and "safe," as in it will always do its best to get you some kind of image onto the screen, rather than throwing exceptions or breaking your game. It also preloads every image only once.

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  
package com.otcsw.alp.utils;

import java.awt.image.BufferedImage;
import java.awt.image.Raster;
import java.util.HashMap;
import java.util.ArrayList;

/**
 * The ImageManager preloads and contains all images used in a particular level.
 * @author Eli Delventhal
 *
 */



public class ImageManager
{
   /**
    * The map that contains all the images that have been preloaded at a given time.
    */

   private static HashMap<String,Sprite> map;
   
   public static void initialize()
   {
      map = new HashMap<String,Sprite>();
      map.put("Default", new Sprite("Images/Front0.png"));
   }
   
   /**
    * Fetches all images for the passed list and places them in the map.
    * @param images   The names of the images to be loaded.
    */

   public static void preloadImages(ArrayList<String> images)
   {
      for (int i = 0; i < images.size(); i++)
         preloadImage(images.get(i));
   }
   
   /**
    * Loads a single image into the map. Generally this will only be called by preloadImages().
    * Duplicates will not be added twice - therefore if it's a question preload an image again.
    * @param name   The name of the image to fetch.
    * @return   Returns whether or not the image was actually loaded.
    */

   public static boolean preloadImage(String name)
   {
      if (!map.containsKey(name))
      {
         map.put(name,new Sprite("Images/"+name));
         return true;
      }
      return false;
   }
   
   /**
    * Returns the specified sprite. This method is safe - if the sprite does not exist,
    * it first attempts to preload it. If that didn't work, it displays the default.
    * @param name   The name (key) of the sprite.
    * @return      The sprite.
    */

   public static Sprite getSprite(String name)
   {
      Sprite s = map.get(name);
      //If the specified sprite didn't exist at all, attempt to preload it.
     if (s == null)
      {
         preloadImage(name);
         s = map.get(name);
         System.err.println("Late preloading: " + name);
      }
      //If the reference file didn't exist, then use the default sprite.
     if (s.getWidth() == 0 || s.getHeight() == 0)
      {
         s = map.get("Default");
         System.err.println("Default sprite loaded: " + name);
      }
      return s;
   }
   
   /**
    * Clears the map of all preloaded images.
    */

   public static void clearMap()
   {
      map.clear();
   }
   
   /**
    * Unloads all of the specified images from the map.
    * @param images   A list of the keys of the images.
    */

   public static void unloadImages(ArrayList<String> images)
   {
      for (int i = 0; i < images.size(); i++)
         unloadImage(images.get(i));
   }
   
   /**
    * Unloads the specified image from the map.
    * @param name   The key of the image to unload.
    */

   public static void unloadImage(String name)
   {
      map.remove(name);
   }
}


I usually couple this with giving every Entity a preloadAll() method, which they can use to tell the ImageManager what it should preload. That way once at initialization you just loop through all your Entitiy's and call preloadAll(). Then later on you just access the image with ImageManager.getImage().

As for having GameMap extend Entity, you can do that if you like but I wouldn't. Instead it's more like this:
1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
public class GameMap
{
     public Entity[][] level;

     public GameMap(int w, int h)
     {
          level = new Entity[w][h];
     }

     public void act()
     {
          for (int i = 0; i < level.length; i++)
               for (int j = 0; j < level[0].length; j++)
                    level[i][j].act();
     }

     public void draw()
     {
          for (int i = 0; i < level.length; i++)
               for (int j = 0; j < level[0].length; j++)
                    level[i][j].draw();
     }
}


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  
public abstract class Entity
{
     private int x;
     private int y;
     private int width;
     private int height;
     private String image;

     public Entity(int newX, int newY, int w, int h, String img)
     {
          x = newX;
          y = newY;
          width = w;
          height = h;
          image = img;
     }

     public abstract void act();

     public void draw()
     {
          drawImage(ImageManager.getImage(image),x,y,width,height);
     }

     public void preloadAll()
     {
          ImageManager.preloadImage(image);
     }
}


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  
public class Unit extends Entity
{
     private int maximumHealth;

     public Unit(int newX, int newY, int w, int h, String img, int maxHealth)
     {
          super(newX,newY,w,h,img);
          maximumHealth = maxHealth;
     }

     public void act()
     {
          for (int i = 0; i < Globals.getGameMap().getEntityCount(); i++)
          {
               Entity e = Globals.getGameMap().getEntityAt(i);
               
               if (e instanceof Enemy)
               {
                    attack(e);
               }
          }
     }

     public void attack(Entity e)
     {
          //blah blah
    }
}


Basically an Entity is something that can exist in your world / GameMap / whatever you want to call it. It doesn't really make sense for the GameMap itself to be an Entity. Instead, it contains entities and gives them access to other entities if they need it.

See my work:
OTC Software
Offline marinepower

Junior Member


Projects: 2



« Reply #9 - Posted 2009-04-28 03:34:39 »

Well, it's been a while, but I think I've accomplished enough work on this to post an update.  As I stated in my first post, this is my first game, so I would like to post what I have so far and see if anyone can run it.  It's an applet, so even though a program might function inside an IDE, it might crash outside of one, which is why I would like some users to test it.  However, I don't have a website, so would like some suggestions about how to put it up for download.

Here's a screen shot:


Regarding the actual game programming, however, I also have a couple of questions that I've had difficulty finding the answers too.  As of now, there are no objectives of any sort, because I wanted each map to be dictated by various triggers encoded into the map.  For example, there could be triggers not just for Mission Victory/ Mission Defeat, but instead, stuff that would also allow scripted events to happen, like if a marine is brought to location 2,2, a moving tank appears for, say, player 1.

Has anyone ever implemented a system like this, or something similar?

Also, I'm drawing a complete blank with how to code TBS ai.  I suppose it only has to be calculated once, but I'm not sure what exactly goes into making a fully functioning ai, that will move units, build structures, and finally end its turn. 

I'm really excited about this game, and can't wait to finish up the complete version!   By the way, if any of you are curious, this game was completely rewritten from the ground up.  The old version was scrapped a few days after demonpant's post, which made me realize just how much my code was repeating itself just to perform a simple task.  It's currently 6000 lines of uncommented code, and like 8k commented.
Games published by our own members! Check 'em out!
Legends of Yore - The Casual Retro Roguelike
Offline fireside

Senior Newbie





« Reply #10 - Posted 2009-04-28 05:24:04 »

What I would do on the site is to do a search for "free web hosting."  You will probably want some sort of site and it's as good a time to get started as any.  Make sure they allow jar files for uploading.  The game looks very nice from the screen shot.
Offline Eli Delventhal

JGO Kernel


Medals: 42
Projects: 11


Game Engineer


« Reply #11 - Posted 2009-04-28 06:00:30 »

For cause/effect stuff and special triggers, looking into using javac inside your game. (I believe you can use the Runtime class...) It can compile and run external Java code, so you can basically just write Java "scripts" and then run them for each level, or you can make your own scripting language and then just turn it into Java, then compile that.

See my work:
OTC Software
Offline Mr_Light

Senior Member




shiny.


« Reply #12 - Posted 2009-04-28 16:31:54 »

tbh every starting programmer should watch this presentation: http://www.youtube.com/watch?v=aAb7hSCtvGw&hl=nl

It's harder to read code than to write it. - it's even harder to write readable code.

The gospel of brother Riven: "The guarantee that all bugs are in *your* code is worth gold." Amen brother a-m-e-n.
Online pjt33
« Reply #13 - Posted 2009-04-29 13:14:35 »

Also, I wanted to know if it is possible to make a png image a different color or rotate it, as there are multiple factions, and I wanted to make all their units and buildings their color.
The way I do this is to say that anything which is really absolutely grey (R=G=B) gets recoloured and anything else is left alone. This allows you to change just e.g. the flag or the uniform. The OO Java-approved way of doing this would be an RGBImageFilter:
1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
public class TintFilter extends RGBImageFilter
{
   private final int r0b;
   private final int g;

   public TintFilter(Color color)
   {
      r0b = color.getRGB() & 0xff00ff;
      g = (color.getRGB() & 0xff00) >> 8;
   }

   public int filterRGB(int x, int y, int rgb)
   {
      if (((rgb ^ (rgb >> 8)) & 0xffff) == 0)
      {
         int val = rgb & 0xff;
         int alpha = rgb & 0xff000000;
         return alpha + (((r0b * val) >> 8) & 0xff00ff) + ((g * val) & 0xff00);
      }

      return rgb;
   }
}


but the more straightforward approach (possibly less memory-efficient - I haven't profiled it) is
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  
public static BufferedImage tint(BufferedImage in, Color tint)
{
   int r0b = tint.getRGB() & 0xff00ff;
   int g = (tint.getRGB() & 0xff00) >> 8;

   int w = in.getWidth();
   int h = in.getHeight();
   int[] data = new int[w * h];
   in.getRGB(0, 0, w, h, data, 0, w);

   for (int i = 0; i < data.length; i++)
   {
      int rgb = data[i];
      if (((rgb ^ (rgb >> 8)) & 0xffff) == 0)
      {
         int val = rgb & 0xff;
         int alpha = rgb & 0xff000000;
         data[i] = alpha + (((r0b * val) >> 8) & 0xff00ff) + ((g * val) & 0xff00);
      }
   }

   final int[] off = new int[]{0xff0000, 0xff00, 0xff, 0xff000000};
   DataBufferInt dbi = new DataBufferInt(data, data.length);
   WritableRaster raster = Raster.createPackedRaster(dbi, w, h, w, off, new Point(0, 0));
   return new BufferedImage(ColorModel.getRGBdefault(), raster, false, null);
}


Warning: untested code, may contain bugs, use at own risk, etc.
Offline teletubo
« League of Dukes »

JGO Ninja


Medals: 48
Projects: 4
Exp: 8 years



« Reply #14 - Posted 2009-04-29 15:00:40 »

marinepower, for a turn based game AI, I would suggest you take a look in this article :
http://www.gamasutra.com/view/feature/1535/designing_ai_algorithms_for_.php?page=3

I am using a similar approach in my project, and it works extremely well. I often get beaten by the AI .
In short, you have a list of priorities to your units - it tries to do first what's on top of priorities list, if it cannot do it, it tries the next .

-----

pjt33, thanks man ! Lucy I read this topic. I was thinking a nice way to do this coloring stuff in my game but never thought of a decent way . I was trying stuff like RGB >200 and stuff like that but there were a lot of undesired coloring . I think absolute gray will work fine ... (tough it'll be a pain in the ass coloring all my sprites ..)

Offline marinepower

Junior Member


Projects: 2



« Reply #15 - Posted 2009-04-29 21:47:55 »

Hmm..  This seems like it could work, or at least something that could be built off of, so thanks for the link.  I'll try implementing it some time next week.  (Gotta study for the actual computer science AB exam first!)

To pjt33:
Yeah, that's exactly how I'm doing my spriting stuff too.  At first I used a color image method that would merge two colors together, but this would also effect the color black (which should always stay black to give something its outline), so I decided on grayscale.  However, this forces you to do everything pixel by pixel, which does take forever.  Now that I think of it, however, it could be possible to convert an image to grayscale using a filter, and then run it through this method.  I'll go try it now.

If any of you are curious, here is what the marine idle animation looks like before its colored:

And then colored:



EDIT:
Grayscale on non-hand drawn images doesn't work that great.  They only look decent when the saturated image has the same color as the original image.
Original:

Randomly colored:

Offline marinepower

Junior Member


Projects: 2



« Reply #16 - Posted 2009-05-01 03:07:47 »

Right now I'm trying to implement triggers, and would like some input on the following way of doing it.  Essentially, the trigger class runs all Action objects stored in it if all Condition objects are met.  So it would perform some sort of loop through the set of conditions, and if all were met, then it would loop through all actions, running each one.

So, a Condition could be something like... having the least amount of funds, having the most units in total, bringing exactly 3 units at a certain location, and an Action could be something like give player $50, create a unit for the player, etc.

However, in order to do something like that, you would have to have some sort of Parameter objects for each Condition.  For example, in the following condition (Current Player) brings (at least) (5) (Infantry) to Location (Bring units here!), all the things in the parenthesis should be parameters.  As they are not numbers, though, I'm not sure how to construct it so it works like that.  I suppose there could be a Quantity extends Parameter object, which could have final ints like AT_LEAST, EXACTLY, AT_MOST, and then an actual value.  Which means that a condition must be able to call a parameter with some sort of a value, say, an int, which should return true if that value meets the conditions, or false other wise.

And for the (infantry) parameter, there could be a UnitParameter, which returns true if a unit its sent meets the data it was initialized with.

It seems like that would most likely be the exact same thing that should be done with Action objects, so perhaps both of them should extend some super class.  Not sure though.


Oh yeah, and for (bring units here!) there could be an Area object, with data types such as a starting point (2,2) and an ending point (5,5), as well as a name.  This would work for testing if an object is in those bounds, but I'm not sure how it would work for creating objects in those bounds.

So, what do you guys think?  Any suggestions before I try to implement this?
Offline ewjordan

Junior Member





« Reply #17 - Posted 2009-05-01 22:24:18 »

So, a Condition could be something like... having the least amount of funds, having the most units in total, bringing exactly 3 units at a certain location, and an Action could be something like give player $50, create a unit for the player, etc.
Here's an initial suggestion, assuming I'm understanding roughly what you need:
1  
2  
3  
4  
5  
6  
7  
/**
 * A condition to test against a certain object.
 */

public interface Condition<T>  {
    /** Return true if the condition holds for the object. */
    public <U extends T> boolean test(U testMe);
}

1  
2  
3  
4  
5  
6  
7  
/**
 * Apply an action to the object.
 */

public interface Action<T> {
    /** Apply the action. */
    public <U extends T> void apply(U toMe);
}


Here's an example Player class:
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  
public class Player {
    static private int playerCount = 0;
    protected int units;
    protected int money;
    private int playerID;

    public Player() {
        playerID = playerCount++;
        units = 0;
        money = 0;
    }

    public Player(int units, int money) {
        this();
        this.units = units;
        this.money = money;
    }

    public int getPlayerID() {
        return playerID;
    }

    public String toString() {
        return ("Player ID " + playerID + ": Units: " + units + "; Money: " + money);
    }

    int getUnits() {
        return units;
    }

    void addMoney(int dollars) {
        System.out.println("Player " + getPlayerID() + " got " + dollars + " dollars!");
        money += dollars;
    }

}

...and to make sure that the generics stuff is right, a subclass of Player, which "cheats" and gets twice as much money as it should:
1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
public class CheatingPlayer extends Player {
    public CheatingPlayer(int units, int money) {
        super(units, money);
    }
    @Override
    void addMoney(int dollars) {
        System.out.println("Player " + getPlayerID() + " was supposed to get " + dollars + " dollars.");
        money += 2*dollars;
        System.out.println("But he cheated, and got " + 2*dollars + " instead!");
    }
}


Finally, the main class, where we define the actual conditions and bonuses inline:
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  
public class Main {
    /** Apply the action if the condition holds true on the passed object. */
    static <T> void applyIf(Action<? super T> action, Condition<? super T> condition, T toMe) {
        if (condition.test(toMe)) action.apply(toMe);
    }

    /** Combine two conditions into one (AND them together) */
    static <T> Condition<T> combineConditions(final Condition<T> first, final Condition<T> second) {
        Condition<T> result = new Condition<T>() {
            public <U extends T> boolean test(U testMe) {
                return (first.test(testMe) && second.test(testMe));
            }
        };
        return result;
    }

    public static void main(String[] args) {
        Player playerOne = new Player( 3 /*units*/, 10 /*money*/);
        Player playerTwo = new Player(6, 10);
        Player cheatingPlayer = new CheatingPlayer(6, 10);

        System.out.println(playerOne);
        System.out.println(playerTwo);
        System.out.println(cheatingPlayer + " (cheater)");

        Action<Player> bonusAction = new Action<Player>() {
            public void apply(Player toMe) {
                toMe.addMoney(10);
            }
        };

        Condition<Player> unitCondition = new Condition<Player>() {
            public boolean test(Player testMe) {
                return (testMe.getUnits() > 5);
            }
        };

        applyIf(bonusAction, unitCondition, playerOne);
        applyIf(bonusAction, unitCondition, playerTwo);
        applyIf(bonusAction, unitCondition, cheatingPlayer);

        System.out.println(playerOne);
        System.out.println(playerTwo);
        System.out.println(cheatingPlayer+" (cheater)");

        Condition<Player> moneyCondition = new Condition<Player>() {
            public boolean test(Player testMe) {
                return (testMe.getMoney() > 25);
            }
        };

        Condition<Player> winCondition = combineConditions(unitCondition, moneyCondition);

        Action<Player> winAction = new Action<Player>() {
            public void apply(Player toMe) {
                System.out.println("Player " + toMe.getPlayerID() + " won!");
            }
        };

        applyIf(winAction, winCondition, playerOne);
        applyIf(winAction, winCondition, playerTwo);
        applyIf(winAction, winCondition, cheatingPlayer);

    }

}


The reason to use generics for something like this is that now you can also define a Condition<Enemy>, or a Condition<Tile>, or anything you'd like.  You'll still have to manually manage the various lists of conditions and which arguments to apply to them (type erasure means that you can't pull the things in brackets out at runtime), which will probably happen in your Trigger class.

I'll be perfectly honest: for stuff like this, Java is a poor language, so no solution will be perfect or clean (and I make no claims that the above suggestion is the best, in fact, it may be severely flawed in some way, I just threw it together very quickly).  What you really asking is to send a piece of code as a parameter to a function, and unless/until Java gets closures this is very messy.  It's very easy to meander down a path where you end up simulating a functional language within Java, and I'd strongly suggest that if you find that happening, you either embed a scripting language into your game (Javascript integrates pretty well, I've heard) or write key parts of it in Scala, Clojure, JRuby, or some other functional language that can interop decently with Java.

You might also look into the Properties pattern (see Yegge's post at http://steve-yegge.blogspot.com/2008/10/universal-design-pattern.html for more than you'd ever care to know about it), which can be useful in games.
Offline marinepower

Junior Member


Projects: 2



« Reply #18 - Posted 2009-05-02 22:13:13 »

Thanks for the info.  Lol i never knew you could call this() within a class. 

I tried to do something similar to your framework, except with all the Actions and Conditions being defined in different classes.  It did seem like I was writing a language inside a language, but there were only four different Parameter classes needed to define all the functionality needed, which were Quantity, PlayerParam, UnitParam, and Area, so it wasn't that big of a deal.  The main classes were implemented like so:

Parameter:
1  
2  
3  
4  
5  
6  
7  
8  
9  
   package CremelianWars.Triggers;
 
  /*A parameter is used to test various pieces of data that are sent to Condition
  objects.*/

    public interface Parameter{
   
       public boolean test(Object other);
   
   }

Quantity:
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  
   package CremelianWars.Triggers;
 
  /*Consists of a number, and the following tests:
  at least
  at most
  exactly*/

 
    public class Quantity implements Parameter{
   
      public static final int AT_LEAST = 0;
      public static final int AT_MOST  = 1;
      public static final int EXACTLY  = 2;
   
      private int value = -1;
      private int comparison = -1;
     
       public Quantity(int val, int comp){
         super();
         value = val;
         comparison = comp;
      }
     
       public boolean test(Object other){
       
         if(other == null)
            return false;
         if(!(other instanceof Integer))
            return false;
     
         Integer input = (Integer)other;
         
         if(getComparison() == -1)
            return false;
           
         if(comparison == AT_LEAST)//num must be greater or equal to comparison
        {
            if(input >= new Integer(value))
               return true;
         }
         else if(comparison == AT_MOST)//num must be less than or equal to comparison
        {
            if(input <= value)
               return true;
         }
         else if(comparison == EXACTLY){//num must equal comparison
           if(input == value)
               return true;
         }
         
                 
         return false;
      }
     
       public boolean test(int num){
         return test(new Integer(num));
      }
   
       public int getValue(){
         return value;
      }
     
       public int getComparison(){
         return comparison;
      }
   }


Condition:
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  
   package CremelianWars.Triggers;
 
 
   import java.util.*;
 
 
  /*Tests a list of input data against the data this condition was defined with.
  It uses Parameter objects to test one another*/

    public abstract class Condition{
     
      public static final int SINGLE = 1;
      public static final int MULTIPLE = 2;
      public static final int CONTINUOUS = 3;
   
      protected ArrayList<Object> parameters;
     
       public Condition()
      {
      }
     
       public abstract boolean isMet();
       
       public void setTestData(ArrayList<Object> params){
         parameters = params;
       
      }
   }


Action:
1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
   package CremelianWars.Triggers;
 
    public abstract class Action{
      int timesExecuted  = 0;
   
       public abstract boolean run();
   
       public int getNumExecutions(){
         return timesExecuted;
      }
   }


A class that extends Condition:
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  
   package CremelianWars.Triggers;
   
   import CremelianWars.*;
   
   /*Tests of a given player has accumulated a certain amount of funds:
   -----------------------------------
   Requires PlayerParam, Quantity
   Tests    Player
   */

    public class Accumulate extends Condition{
      PlayerParam activePlayer;
      Quantity compQuantity;
   
       public Accumulate(PlayerParam p, Quantity q){
         super();
         activePlayer = p;
         compQuantity = q;
      }
       public boolean isMet(){
         try{
            Player p = (Player)parameters.get(0);
            if(activePlayer.test(p))
               if(compQuantity.test(p.getFunds()))
                  return true;
         
            return false;
         
         
         }
             catch(Exception e){
               System.out.println(e);
               return false;
            }
     
      }
   }


Basically, this implementation just cascades tasks to break them up into smaller and smaller segments, them being trigger -> condition -> Parameter.  But because all of the conditions deal with different things, it seemed like a good idea to use an ArrayList of objects.  Since that's the case, though, its almost impossible to make these function within some sort of a loop, since they all have variable needs.  Typing them out isn't easy either.  I tried implementing the system, and it took a good deal of work to make the system perform a simple task, which looked like this:
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  
   package CremelianWars.Triggers;


   import java.awt.*;
   import java.util.*;


   import CremelianWars.Unit.*;
   import CremelianWars.*;

    public class TriggerTest{
       public static void main(String[] args){
       
         Random rand = new Random();
       
         Trigger t = new Trigger();
       
       //The initialized player's turn is set to 3
        Player p = new Player(3, false);
         
         
         //A map is filled with units controlled by randomized players
        GameMap map = new GameMap(3,3);
         for(int x=0;x<map.getWidth();x++)
            for(int y=0;y<map.getHeight();y++)
               map.getLoc(new Point(x,y)).setUnit(new Unit(2,2,rand.nextInt(4),2));
         
         
         //Creates three necessary Parameters for the control condition
        Parameter unitParam= new UnitParam(-1, UnitParam.UNIT);
         Parameter playerParam = new PlayerParam(p.getTurn(), PlayerParam.SPECIFIC);
         Parameter quantity = new Quantity(2, Quantity.AT_LEAST);
         
         //Creates the conditions from the parameters
        Condition control = new Control(unitParam, playerParam, quantity);
         Condition elapsed = new ElapsedTime(new Quantity(2, Quantity.EXACTLY));
         
         //adds the conditions to the Trigger
        t.addCondition(control);
         t.addCondition(elapsed);
         
         //sets the test data for the Control condition
        ArrayList<Object> input = new ArrayList<Object>();
         input.add(map);
     
         control.setTestData(input);
         
         
         //sets the test data for the ElapstedTime (in turns) condition
        ArrayList<Object> elapsedInput = new ArrayList<Object>();
         elapsedInput.add(new Integer(1));
         
         elapsed.setTestData(elapsedInput);
         
         
         System.out.println(t.allConditionsMet()+","+control.isMet());
         System.out.println(elapsed.isMet());
      }
   }
   




 Which brings me to my next point of how to use them in a map editor.  I suppose there could be a toString method associated with each, as well as a buildFromString method, so the user could type them in.  XML would probably work better, but the map is already loaded using a similar method dealing with break chars.  So, does there need to be some sort of a static factory method to create these, or do you guys see another way?

Oh yeah, and in the above piece of code, below the comment:"//Creates the conditions from the parameters",
The compiler displays that its illegal to pass those three Parameters to the Control Condition. It would probably be beneficial if there was someway around this, as to allow for more generic objects to be passed.

The Control condition is defined like so:
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  
   package CremelianWars.Triggers;
   
   
   import java.util.*;
   
   import CremelianWars.*;
   import CremelianWars.Unit.*;

   
   /**Tests if a player has a certain amount of units:
   -----------------------------------
   Requires UnitParam, PlayerParam, Quantity
   Tests    GameMap
   
   This method loops through the game map, getting every location, testing if
   the unit it contains meets the test of the unit parameter, and if it does,
   adding it to a total, which is then tested against a quantity.
   */

   
   
    public class Control extends Condition{
      UnitParam   unitParam;
      PlayerParam playerParam;
      Quantity        quant;
   
       public Control(UnitParam u, PlayerParam p, Quantity q){
         super();
         unitParam   = u;
         playerParam = p;
         quant       = q;
      }
       public boolean isMet(){
         try{
            GameMap map = (GameMap)parameters.get(0);
           
            ArrayList<Unit> allUnits = map.getAllUnits();
           
            int controls = 0;
           
            for(Unit u : allUnits){
               if(unitParam.test(u))
                  if(playerParam.test(u.getPlayer()))
                     controls++;
            }
           
            if(quant.test(controls))
               return true;
         
            return false;
         
         
         }
             catch(Exception e){
               System.out.println(e);
               return false;
            }
     
      }
   }
Pages: [1]
  ignore  |  Print  
 
 
You cannot reply to this message, because it is very, very old.

 

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

The first screenshot will be displayed as a thumbnail.

theagentd (6 views)
2014-04-24 23:00:44

xsi3rr4x (83 views)
2014-04-15 18:08:23

BurntPizza (75 views)
2014-04-15 03:46:01

UprightPath (86 views)
2014-04-14 17:39:50

UprightPath (69 views)
2014-04-14 17:35:47

Porlus (86 views)
2014-04-14 15:48:38

tom_mai78101 (109 views)
2014-04-10 04:04:31

BurntPizza (169 views)
2014-04-08 23:06:04

tom_mai78101 (265 views)
2014-04-05 13:34:39

trollwarrior1 (217 views)
2014-04-04 12:06:45
Escape Analysis
by Roquen
2014-04-25 10:38:58

Escape Analysis
by Roquen
2014-04-25 10:22:13

List of Learning Resources
by SHC
2014-04-18 03:17:39

List of Learning Resources
by Longarmx
2014-04-08 03:14:44

Good Examples
by matheus23
2014-04-05 13:51:37

Good Examples
by Grunnt
2014-04-03 15:48:46

Good Examples
by Grunnt
2014-04-03 15:48:37

Good Examples
by matheus23
2014-04-01 18:40:51
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!