Jump to content

  • Log In with Google      Sign In   
  • Create Account

de_mattT

Member Since 07 Jun 2012
Offline Last Active May 16 2014 03:06 PM

#5050105 SOLID and game (engine) design

Posted by de_mattT on 04 April 2013 - 03:20 PM

Single Responsibility Principle

I'd rather go with that single responsibility is when "a class performs on the same set of data".

 

I find SRP difficult. I do use "a class performs on the same set of data" as an indication of the number of responsibilities a class has.

 

You can't always look at a class in isolation and determine how many responsibilities it has, because the number of responsibilities is often dependent on how the class is used by its clients.

 

It's a good idea to look at the clients that are using your class and to see how much of the class each client is using, and how they are using it. This may give you an idea of the responsibilities that the clients see the class as having.

 

Remember also that a class can have multiple responsibilities (particularly if they are all realised using the same data members), but the responsibilities can be separated by creating a pure virtual class for each responsibility which the class inherits. Clients can then use the pure virtual class in place of the original class so that they are only aware of the parts of the class that they use.

 

Matt




#5050098 SOLID and game (engine) design

Posted by de_mattT on 04 April 2013 - 02:56 PM

Dependency Inversion Principle

2. In case this is a correct approach, when and where should I apply this? Should I really create an "interface" for each and every dependency I might have, if not, how do I destinquish between cases where it makes sense and where not?

 

'interface' means the portion of a class that is publicly visible.  The interface does not have to be a separate pure virtual class (interface in Java / c#).

 

The key idea behind the Dependency Inversion Principle is that the clients impose the interface on the servers, not the other way around. The interface is owned by the clients and is designed to meet their needs.

 

In an attempt to adhere to this principle I started introducing pure virtual classes everywhere, but I realised that they were adding unnecessary complexity. As long as the server's interface is imposed by the client it doesn't matter if the interface is defined separately by a pure virtual class or not.

 

I generally treat pure virtual classes exactly as I would treat any super class. The top level class of an inheritance hierarchy should contain as much common functionality as possible (Don't Repeat Yourself). The application of the DRY principle usually dictates the type of class that will sit at the top of my inheritance hierarchies.

 

In saying that, sometimes a pure virtual class makes sense, as a pure virtual class describes what an object does, and not what an object is.

 

These principles are tools to help you manage the complexity of your design. They are a guide only and applying them effectively comes with experience.

 

Matt




#5023593 Version control for begginers

Posted by de_mattT on 20 January 2013 - 01:40 PM

samoth makes a good point that you need to consider your needs before selecting the version control system that is right for you.


It's always good to learn something new, and exposure to version control software is definitely a good thing for a future career in software development, but one option open to you is to not use any version control software. From your post it seems that you are kean to focus your attention on the development of your game, so perhpas you could simply use .zip files to create snapshots of your code at key moments (e.g. every major release). This may suit your needs just fine.




#5008870 [JAVA] image rotation

Posted by de_mattT on 09 December 2012 - 02:20 PM

Inded the g2d object represents the entire contents of the JPanel - it's the canvas upon which everything is drawn.

Rather than everything having its own g2d object, everything should have its own AffineTransform object to define where it should be drawn on the canvas.

It's a good idea to reset the transformation after you've drawn your image (I think this will fix your problem) as follows:

public void draw(Graphics2D g2d) {
	  rotate.translate(location.x, location.y);
	  rotate.rotate(rotation);
	  rotate.translate(-location.x,-location.y);
	
	  // take a copy of the transformation that g2d is using
	  AffineTransform prevTransform = g2d.getTransform();

	  // apply the transformation for this object and draw
	  g2d.setTransform(rotate);
	  g2d.drawImage(currentAnimation.getCurrentFrame(), (int) location.x, (int) location.y, null);

	  // revert g2d back to the old transformation
	  g2d.setTransform(prevTransform);
}

Matt


#5007218 Questions about code efficiency and collision detection

Posted by de_mattT on 04 December 2012 - 05:05 PM

1) Public declared class variables vs class properties
Hopefully you've encountered Encapsulation and information hiding. As is described in the link, your first approach allows objects to control access to their data, which allows the object to ensure that the data is only ever set to valid values. In your "set" property you could perform some validation checks on value before updating tileRectangle. Also if you decided to change how the bounding box is stored, in the second method you would have to update every piece of code which uses bounding box, whereas in your first example you would only need to change the properties. This is related to Once and Only Once (DRY).

Note in your first example it would be good to declare tileRectangle as private. It may be by default but it's always good to be expressive.

2) Methods with multiple parameters vs overloaded methods.
Your first approach may cause issues with maintainability. If you decided to add other game object types that are also subject to collision checks (perhaps you add a Soldier class), you would have to add a new parameter to 'Collision' for each new game object type. Your parameter list could get very long. Even worse, every time you add a new collidable game object type you would have to modify every call to 'Collision' to specify the value for the new parameter (e.g. FALSE). Your second approach avoids this problem. I would probably go one step further though and give my methods more expressive names like "isTankColiding" and "isProjectileColiding".

3) Another "battle" question: List<T> vs Array
I would tend to use arrays only for very simple tasks. Anything slightly complicated requiring data structure management (e.g. inserting, sorting, removing an element in the middle of the structure) and I would use a list. Favor keeping your code simple and expressive over performance. Write good clean code first and then only change it if testing / profiling show that that piece of code is not fast enough. In 99% of cases the List will do just fine. Don't waste valubale time re-inventing the wheel, keep it simple.

4) Events and event handler.
Events are a way for a class to signal to other interested classes that something has happened. The Observer pattern is an example of a simple event handling architecture.
C# contains an event construct which makes it simple to create your own events. Maybe there are lots of classes in your game which will need to know when the bounding box of a tank is updated. Those classes can implement an event handler method (perhaps called boundingBoxUpdateHandler) to describe what they will do when they hear about the BoundingBoxUpdated event, and then can register this event handler method with the tank object which contains the bounding box. Then when the bounding box is updated, the tank object can loop through all the registered event handlers and let them know that the event has occurred.

5) And final, my most important question: collision detection.
I'm a bit confused on the way that you're calculating the offset variable. If the Tank is more than the totalWidth or totalHeight from the origin, won't your x and y indexes go out of range? E.g. in the following loop if tankBox.left > offset.X + totalWidth, x will not be a valid index for wallColor and tankColor. I've probably missed something, it's late :)

// Comparsion for collision
    for (int x = tankBox.Left - offset.X; x < tankBox.Right - offset.X; x++)
	    for (int y = tankBox.Top - offset.Y; y < tankBox.Bottom - offset.Y; y++)
		    if (wallColor[x, y].A != 0 &amp;&amp; tankColor[x, y].A != 0)
			    return true;

If your PixelPerfectCollision method is a problem performance-wise then you could combine the first two for loops together, and you could combine the last two for loops together.

I haven't done pixel-perfect collision detection before but I would start by comparing bounding boxes to check that the tank is colliding with a particular wall segment, and if the bounding boxes are colliding only check the area where the two bounding boxes overlap for a collision. Also I would check one wall at a time so that if you find a collision early on you can return true and don't have to check the other walls.

Hope this is useful

Matt


#5003004 How to make a "Level 2" with JFrame

Posted by de_mattT on 21 November 2012 - 01:56 PM

You could pass a reference to Main into the Level1 constructor, and then call a method in main when the boss is killed. E.g.

public class Level1
{
	 // store a reference to main so that main can be informed when
         // level is complete
	 private Main main = null;

	 // constructor takes initMain as input. initMain is informed when
	 // an event (e.g. boss killed) happens
	 public Level1(Main initMain)
	 {
	 	 main = initMain;
	 }

	 ...

	 public bossKilled()
	 {
	 	 // inform main that the level is complete
	 	 main.handleLevelEvent("BOSS_KILLED");
	 }
}

public class Main extends JFrame
{
	 // move frame outside of Main constructor so that all methods
	 // can see it
	 private JFrame frame = null;

	 public Main()
	 {
		  frame = new JFrame();

		  // Pass reference to Main to level1
		  frame.add(new Level1(this));
		  frame.setTitle("Save little L");
		  frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		  frame.setSize(1024,768);
		  frame.setVisible(true);
	 	 frame.setLocationRelativeTo(null);
	 }

	 public static void main(String[] args)
	 {
	 	 Main m = new Main();
	 }

	 public void handleLevelEvent(String eventName)
	 {
	 	 if(eventName == "BOSS_KILLED")
	 	 {
	 	 	 // change to level 2
	 	 	 frame.remove(0);
	 	 	 frame.add(new Level2());
	 	 }
	 }
}

The code above forms the basic mechanism of the Observer (sometimes called Listener) design pattern.
The full Observer pattern is only slightly more complex - check out wikipedia for details.

I notice that the Component class has a protected method "firePropertyChange" which achieves the same kind of thing using the PropertyChangeListener interface. Perhpas you could make use of this feature.

Matt

Edit - Corrected to clarify that the code example is not an example of the observer pattern, but uses the mechanism at the heart of the pattern.


#5002446 General Class Structure Question

Posted by de_mattT on 19 November 2012 - 03:32 PM

Hi Bedtime,

I'm having some trouble following your "derives" relationships above. Are you asking if everything should inherit from "Game" so that an instance of "Game" doesn't have to be passed in function calls where it is used?

My general thoughts would be:
1. Inheritance is an "Is A" relationship, therefore a child object should only inherit from a parent object if it is a more specialised version of the parent object. So I would think that, based on your overview above, nothing would inherit from Game.
2. Would "Has A" relationships work instead? Is it possible to pass in the "Game" instance to "Draw" at initialisation and store the reference for later use? Or are you using a single "Draw" object to do the drawing for all split-screen games?

Matt


#5001641 [SOLVED]Triangular Rotation\Creating a "Circle" out of Triangles

Posted by de_mattT on 16 November 2012 - 03:30 PM

Here's the algorithm I'm using to perform the same kind of thing you're doing but closer to the way suggested by #2 Sik_the_hedgehog. I've inlined some support methods so it's all in one place so hopefully it still works :).

(Java code)

public static Polygon createRegularPolygon(int numberOfSides,
   double sideLength, double dblStartAngleInDegrees)
{
   // slice angle
  double dblSliceAngle = (2 * Math.PI) / numberOfSides;
 
  // calculate circum radius
  double dblRad = sideLength / (2 * Math.cos( (180 - dblSliceAngle) / 2);

  // calculate the start angle in radians for repeated use in the for loop
  double startAngleInRadians = Math.toRadians(dblStartAngleInDegrees);
 
  // now define the polygon
  for (int i = 0; i < numberOfSides; ++i)
  {
   double dblAngle = startAngleInRadians + (dblSliceAngle * i);
   int x = (int) Math.round(Math.cos(dblAngle) * dblRad);
   int y = (int) Math.round(Math.sin(dblAngle) * dblRad);
   
	 // store x and y somewhere for later use...
  }
}

Matt


#5001640 [SOLVED]Triangular Rotation\Creating a "Circle" out of Triangles

Posted by de_mattT on 16 November 2012 - 03:19 PM

glVertex2d(0.0 - (FindMissingSide(r, r, 360.0/slices)/2), 0.0 - r);
glVertex2d(0.0 + (FindMissingSide(r, r, 360.0/slices)/2), 0.0 - r);


I think your y values might be the problem.
Your y values are 0-0 - r (the circumRadius)
But what you want is 0-0 - h (the Apothem) - the height of the triangle in your case.

Where h is calculated using pythagoros as:
h = sqrt(pow(r, 2.0) + pow(FindMissingSide(r, r, 360.0/slices)/2, 2.0)

Wikipedia's got a good description of regular polygon's which I found pretty useful. http://en.wikipedia.org/wiki/Regular_polygon

Hope this works.

Matt


PARTNERS