Pages: 1 2 3 4 5 6 7 8 9 10
Author Topic: Tiering vs Components  (704,345 Views)
Offline (Unknown gender) Josh @ Dreamland

Prince of all Goldfish
Developer
Joined: Feb 2008
Posts: 2,950
View profile
Posted on: May 08, 2010, 05:37:01 PM
It looks like it's time once again for a poll.
Before voting, give Miky / Rusky a chance to present their idea of a Component system, as applicable to ENIGMA.
If you have a separate proposal, post it in the same format as I am about to post mine. This format should include the following:

- Code for an object called object0 containing a local "my_local"
----- This should include ALL code required to include necessary locals, particularly those needed throughout the rest of this demonstration
- Code for a simple collision function, check_collision(int id), returning bool, utilizing these hypothetical functions:
----- get_object_by_id(int), returning whatever the system requires (probably a pointer)
----- check_rotated_rectangle(x,y,bbox_left,bbox_right,bbox_top,bbox_bottom,image_angle); returning bool
- A create event code that shows message "Hello, world! I am at (<x>,<y>)", as it will look when compiled.

My code will have the official names in it, except where a substitute has been given above explicitly. It doesn't really matter what return types are if not specified.

DO NOT vote before a similar description is available for the Component system.

Moreover, I'd say requesting additional code on the behalf of either/any party is fair game.
Offline (Unknown gender) Josh @ Dreamland

Prince of all Goldfish
Developer
Joined: Feb 2008
Posts: 2,950
View profile
Reply #1 Posted on: May 08, 2010, 05:41:48 PM
Tier System

Instantiable code:
struct ultimate_tier : collision_tier //This includes collision and all lower tiers
{
  //worthless locals shared by all objects go here.

  ultimate_tier(): collision_tier() { } //takes care of id and object_index, which are const; myevent_create is in this case inherited from the first tier.
  ultimate_tier(const unsigned ID, const int ObjectID): collision_tier(ID,ObjectID) { } //takes care of id and object_index, which are const
}

struct OBJ_object0 : ultimate_tier //This again includes collision and all lower tiers
{
  var my_local; //Here is the local we're using
 
  enigma::variant myevent_create();
 
  OBJ_object0(): ultimate_tier() { myevent_create(); } //takes care of id and object_index, which are const; myevent_create is in this case inherited from the first tier.
  OBJ_object0(const unsigned ID, const int ObjectID): ultimate_tier(ID,ObjectID) { myevent_create(); } //takes care of id and object_index, which are const
}


Collision code:
#include "collision_tier.h"
bool check_collision(int id)
{
    object_basic *o = get_object_by_id(int); //Fetch an instance of a pointer to the type of the lowermost tier from the instance array
    object_collision *c = (object_collision*) o; //Assume that it contains locals for our tier.
    return check_rotated_rectangle(c->x,c->y,c->bbox_left,c->bbox_right,c->bbox_top,c->bbox_bottom,c->image_angle); //Check and return
}


Create event code:
enigma::variant OBJ_object0::myevent_create()
{
  show_message("Hello, world! I am at (" + string(x) + "," + string(y) + ")");
}
Offline (Unknown gender) Rusky

Resident Troll
Joined: Feb 2008
Posts: 954
View profile WWW
Reply #2 Posted on: May 08, 2010, 06:28:06 PM
Component System

struct SpriteComponent {
  virtual ~SpriteComponent(int _x, int _y) : x(_x), y(_y) {}
  virtual void draw() = 0;
 
  int x, y;
  int bbox_left, bbox_right, bbox_top, bbox_bottom;
  int image_angle, image_alpha; // ...
};

struct RasterSprite : public SpriteComponent {
  RasterSprite(Sprite& s, int x, int y) : sprite(s), SpriteComponent(x, y) {}
  void draw(); // pretend this
  Sprite& sprite;
};


struct CollisionComponent {
  virtual ~CollisionComponent() {}
  virtual bool check_collision(int id) = 0;
};

struct Colligma : public CollisionComponent {
  Colligma(SpriteComponent& s) : sprite(s) {}
  bool check_collision(int id);

  SpriteComponent& sprite;
  Sprite& mask_index;
};

// moved check_collision into the collision component
// it uses the sprites instead of manually unpacking all the fields
// check_rotated_rectangle would probably be part of Colligma
// you could replace it with a vector collision system in another component
bool Colligma::check_collision(int id) {
  Object& o = get_object_by_id(id);
  return check_rotated_rectangle(o.collision.sprite, sprite);
}


struct Object {
  Object(const unsigned _id, const int obj, SpriteComponent& s, CollisionComponent& c) : id(_id), object_index(obj), sprite(s), collision(c) {}
  virtual ~Object() {}
 
  int& x() { return sprite.x; }
  int& y() { return sprite.y; }
  // the rest of the accessors go here
 
  unsigned id, const int object_index;
  SpriteComponent& sprite;
  CollisionComponent& collision;
};

// just... pretend we have a real SpriteComponent implementation
struct OBJ_object0 : public Object {
  OBJ_object0(int x, int y, const unsigned id, const int obj) : sprite(enigma::get::my::sprite::somehow, x, y), collision(sprite), Object(id, obj, sprite, component) {}
  variant myevent_create();

  RasterSprite sprite;
  Colligma collision;

  var _my_local;
  var& my_local() { return _my_local; }
};

variant OBJ_object0::myevent_create() {
  show_message("Hello world! I am at(" + string(x()) + ", " + string(y()) + ")");
}
Offline (Unknown gender) retep998

Member
Joined: Jan 2010
Posts: 248
View profile
Reply #3 Posted on: May 08, 2010, 06:30:05 PM
I understand Josh's code.
I don't understand Rusky's code.
Therefore I vote for tier system.
Offline (Unknown gender) Rusky

Resident Troll
Joined: Feb 2008
Posts: 954
View profile WWW
Reply #4 Posted on: May 08, 2010, 06:34:07 PM
I understand my abacus.
I don't understand my TI-83.
Therefore my abacus is better.
Offline (Unknown gender) RetroX

Master of all things Linux
Contributor
Joined: Apr 2008
Posts: 1,055
View profile
Reply #5 Posted on: May 08, 2010, 06:34:53 PM
Rusky, this is C++.

Not Java.

Java teaches people terrible programming techniques and is therefore terrible
Offline (Unknown gender) Rusky

Resident Troll
Joined: Feb 2008
Posts: 954
View profile WWW
Reply #6 Posted on: May 08, 2010, 06:35:27 PM
I wrote C++ with great programming techniques. What's the problem?
Offline (Unknown gender) Josh @ Dreamland

Prince of all Goldfish
Developer
Joined: Feb 2008
Posts: 2,950
View profile
Reply #7 Posted on: May 08, 2010, 06:40:40 PM
Problems:
- Lots of "pretending," which I will assume can be overlooked.
- "sprite.x", "sprite.y"; am I to traverse every pointer in the system to determine what locals are implemented? In the tier system, I just check ancestors, which is natural. Here, it seems I'm expected to check all members, too.

Perhaps it's also good to show an instance_create(), which is generated code. For this, we can assume the constructor takes an X and Y parameter as well as the other two (instance_create is designed for a 2D system, hence parameters X and Y), and adds the instance into the network for execution. If this is incompatible with a component system, give an alternative.

instance_create(): Tier System


void instance_create(int x, int y, int id)
{
  switch (id)
  {
    case object0:
        new OBJ_object0(x,y,enigma::max_id++,id);
      break;
  }
}


Offline (Unknown gender) Rusky

Resident Troll
Joined: Feb 2008
Posts: 954
View profile WWW
Reply #8 Posted on: May 08, 2010, 06:44:47 PM
-Eh, you did more pretending. You didn't even show the actual collision tier.
-x and y are built-ins. You can either 1) move them back out into Object or 2) parse them into sprite.x and sprite.y. As you saw, all non-built-ins go in OBJ_object0 exactly like your system.
-What does instance_create have to do with this? I just left off the constructor overload, which shouldn't matter because you just pretended it and it's not crucial to the component idea.
Offline (Unknown gender) Josh @ Dreamland

Prince of all Goldfish
Developer
Joined: Feb 2008
Posts: 2,950
View profile
Reply #9 Posted on: May 08, 2010, 06:48:52 PM
Here is the snippet, as functions right now in ENIGMA:
#ifndef _COLLISIONS_OBJECT_H
#define _COLLISIONS_OBJECT_H

#include "transform_object.h"

namespace enigma
{
  struct object_collisions: object_transform
  {
    //Bit Mask
      var mask_index;
      var  solid;
   
    //Bounding box
      var bbox_top;
      var bbox_bottom;
      var bbox_left;
      var bbox_right;
   
    //Constructors
      object_collisions();
      object_collisions(unsigned, int);
      virtual ~object_collisions();
  };
}

#endif


I can not move them back into Object, as that will create an access problem and/or screw up your component. They will be accessing different instances of like-named variables.
How do I know they are sprite.x and sprite.y? What tells me that?
Yes, that was obvious.

Also, instance_create shows me from where you are exhuming the initial parameters to your constructors.
Offline (Unknown gender) RetroX

Master of all things Linux
Contributor
Joined: Apr 2008
Posts: 1,055
View profile
Reply #10 Posted on: May 08, 2010, 06:55:27 PM
Quote from: Rusky on May 08, 2010, 06:35:27 PM
I wrote C++ with great programming techniques. What's the problem?
No, you didn't.

Creating loads of objects purely for organization is not good programming technique.

There are easier, more efficient ways of organizing things, and in fact, it removes readability rather than adds it.
Offline (Unknown gender) Micah

Resident Troll
Joined: Jun 2008
Posts: 128
View profile
Reply #11 Posted on: May 08, 2010, 07:00:13 PM
Quote from: RetroX on May 08, 2010, 06:55:27 PMThere are easier, more efficient ways of organizing things, and in fact, it removes readability rather than adds it.
Please show us these ways, retaining the flexibility of this one and adding readability as you claim they do.
Offline (Unknown gender) Josh @ Dreamland

Prince of all Goldfish
Developer
Joined: Feb 2008
Posts: 2,950
View profile
Reply #12 Posted on: May 08, 2010, 07:01:01 PM
Retro: The point only seems to be organization on this level; this is about adding and removing functionality with as little a ripple as possible.

So yes, I'm still waiting on an explanation for the origin of "sprite." and the constructor parameters in all that mess.
Offline (Unknown gender) luiscubal

Member
Joined: Jun 2009
Posts: 452
View profile
Reply #13 Posted on: May 08, 2010, 09:01:38 PM
Both systems are pretty simple to understand:

Josh's tiers: Think of a stack of pizzas. Each pizza is functionality. The graphics pizza, the collisions pizza, etc. On top of everything, you can put your object pizzas.
Removing/Replacing tiers in the middle might be problematic, although Josh seems to consider those problems edge cases and, therefore, unlikely to be harmful and/or too much inconvenient.

Rusky's components: Instead of pizzas on top of each other, think a bunch of blocks side by side. Because they are not on top of each other, they will not fall if you take on from the middle.
On top of those blocks is the object block.
Rusky's system is designed so that replacing blocks is embarrassingly easy(and removing blocks isn't too complex), this at the expense of a few extra CPU cycles, which Rusky seems to consider irrelevant because CPU cycles are very fast and, therefore, the difference is unlikely to be noticeable.

Creating loads of objects purely for organization is not good programming technique.
I have to disagree. Abstractions are part of computer science and engineering.
Otherwise, we would still be coding the computer directly, without programming languages not even kernels.
Offline (Unknown gender) Rusky

Resident Troll
Joined: Feb 2008
Posts: 954
View profile WWW
Reply #14 Posted on: May 08, 2010, 09:08:59 PM
Added the id stuff and a bit of sprite implementation. I don't see why that confused you.

Retro: The *Component classes are interfaces. They're just there to be the parent of all possible implementations. A sprite component could hold a DX handle, a GL handle, some vector data, etc. and implement its draw method accordingly.

Individual objects (like OBJ_object0) create their own components as members or on the heap or wherever you want. They get passed to Object's constructor, and other Object constructor parameters go directly there along with them rather than being passed up through all the tiers.
Pages: 1 2 3 4 5 6 7 8 9 10