This section allows you to view all posts made by this member. Note that you can only see posts made in areas you currently have access to.
Pages: « 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 »
511
Ideas and Design / Re: ENIGMA Entity-Component-System?
« on: November 29, 2013, 10:35:17 am »
I was pointing out that using multiple inheritance does not stop you from constructing methods that work with instances of each "component" class. You were showing that inheritance is not what you want by pointing out that class have functions instead of functions working with classes. It works either way. Java functions take advantage of functions requiring some interface all the time, and it doesn't matter what you hand the function as long as it implements that interface. Yes, an interface will have functions, but that doesn't mean it can't work like the component system.
In other words, while it's more correct to put the doTankAI method in the tank interface, there's nothing stopping you from putting it in a non-member function, which is necessary if you want a doNinjaTankAI method.
So in that outline, Operation Y might be doNinjaTankAI, Subtype A might be Tank, Subtype D might be Ninja, and their operations 1-3 might be getTankHealth, getNinjaHealth, fireCannon, throwStars, whatever.
In other words, while it's more correct to put the doTankAI method in the tank interface, there's nothing stopping you from putting it in a non-member function, which is necessary if you want a doNinjaTankAI method.
So in that outline, Operation Y might be doNinjaTankAI, Subtype A might be Tank, Subtype D might be Ninja, and their operations 1-3 might be getTankHealth, getNinjaHealth, fireCannon, throwStars, whatever.
512
Proposals / Re: New Export Options
« on: November 29, 2013, 10:02:13 am »
To my knowledge, no one has any plans of tackling them in the near future. Most of this is because none of us want to install Solaris or Chrome on anything, and I don't think any of us own a mobile PlayStation device. I own a Wii, and had started a Wii port at one point, but I can't focus on such an obscure platform while the main platforms are in chaos.
I don't speak for everyone, of course, but I'm sure they have somewhat similar sentiment.
I don't speak for everyone, of course, but I'm sure they have somewhat similar sentiment.
513
Tips, Tutorials, Examples / Re: GML Equivalent Commands for GUI Actions
« on: November 24, 2013, 11:53:49 am »
You don't need a random number table. Choose picks a random number and then accesses that element in the va_list (or more specifically, enigma::varargs).
I recommend [snip=EDL]if (random(n) < 1)[/snip] over [snip=EDL]if (ceil(random(n)) == 1)[/snip] for non-aesthetic reasons, too. A true RNG will return values in [0,1). So if I use your code for "with a chance in 1 perform the next action," it will fail with some small probability (when the RNG returns zero). Mine only fails if the RNG is broken and returns one. You could just use <= if that is the case, of course.
I recommend [snip=EDL]if (random(n) < 1)[/snip] over [snip=EDL]if (ceil(random(n)) == 1)[/snip] for non-aesthetic reasons, too. A true RNG will return values in [0,1). So if I use your code for "with a chance in 1 perform the next action," it will fail with some small probability (when the RNG returns zero). Mine only fails if the RNG is broken and returns one. You could just use <= if that is the case, of course.
514
Tips, Tutorials, Examples / Re: GML Equivalent Commands for GUI Actions
« on: November 20, 2013, 10:24:57 am »
Use [snip=EDL]if (random(n) < 1)[/snip] rather than [snip=EDL]if (ceil(random(n)) == 1)[/snip].
515
Ideas and Design / Re: ENIGMA Entity-Component-System?
« on: November 06, 2013, 07:48:35 pm »
In a dynamic bitset, [snip]entity.components | system.required_components[/snip] is O(N). One OR for each 64 bits. So only one OR is required if you have less than 65 components.
Typically the number of components you need in a combination will be small. If not, you might want to rethink how you're developing your system. A function depending directly on some thirty classes isn't very attractive, to say the least.
Typically the number of components you need in a combination will be small. If not, you might want to rethink how you're developing your system. A function depending directly on some thirty classes isn't very attractive, to say the least.
516
Proposals / Re: New Export Options
« on: November 05, 2013, 03:21:47 pm »
ENIGMA is liable to run on the first two platforms with little modification. Our resident idiot nearly got it running on BSD on his own, so Solaris isn't much of a stretch, and I doubt Chrome OS is, either.
Consoles will not be a focus until behavior on the main platforms is consistent. In the past, interested users have ported ENIGMA to a number of mobile platforms, including Android, iOS, Sony PSP, and Nintendo DS. I once had small technical demos running on Wii. These have been, and will likely remain, toy projects, until people can actually install and run ENIGMA on Windows, Mac, and Linux without issue.
Consoles will not be a focus until behavior on the main platforms is consistent. In the past, interested users have ported ENIGMA to a number of mobile platforms, including Android, iOS, Sony PSP, and Nintendo DS. I once had small technical demos running on Wii. These have been, and will likely remain, toy projects, until people can actually install and run ENIGMA on Windows, Mac, and Linux without issue.
517
General ENIGMA / Re: Creation Orders
« on: November 05, 2013, 12:09:13 pm »
The order of special events cannot be set by events.res, presently. It's possible we could do it down the road, but to best ease the translation process, we should implement whatever fix is required automatically based on the filetype being loaded.
I don't care if that's done by configuring events.res or changing events from each file to the events run in the correct order.
I don't care if that's done by configuring events.res or changing events from each file to the events run in the correct order.
518
General ENIGMA / Re: Creation Orders
« on: November 05, 2013, 10:31:58 am »
The issue is that Studio's order makes chronological sense, but not functional sense. To facilitate overwriting variables' default or generic values, users like to be able to run their code from least specific to most specific. That order is as follows:
Object create (for any old reason)
Room creation code
Room start (Some room started)
Game start (The first room started)
Instance create (You are instance 1000043 being created in your original room)
However, chronologically, that order falls apart. How is the room starting before the game?
What needs done is creating a separate set of events for the specificity track and load into them appropriately from each GM format.
Object create (for any old reason)
Room creation code
Room start (Some room started)
Game start (The first room started)
Instance create (You are instance 1000043 being created in your original room)
However, chronologically, that order falls apart. How is the room starting before the game?
What needs done is creating a separate set of events for the specificity track and load into them appropriately from each GM format.
519
Ideas and Design / Re: ENIGMA Entity-Component-System?
« on: November 03, 2013, 09:35:06 pm »
Yes, where M is a small constant. Using a bit set, it drops to O(N × M/64) = O(N × M), assuming you can compact all the combinations together in one word. I'm not fundamentally opposed to a bitset, but I don't particularly care for RTTI.
Using queues trades checks for memory. Instead of iterating the smallest queue of objects and checking that (A) all component bits are set or (B) the entity is in all the correct queues, we just iterate the queue with the correct combination. This is good when the number of components needed in a single combination is large, but the number of necessary unique combinations is small.
In the general case, it's terrible, as the number of combinations grows exponentially in the number of behaviors. You'd want to use queues when you need to iterate all tanks which use tank logic and have tank physics and a position, and all ninjas which use ninja logic and have ninja physics and a position, but no other combinations. In this case, you'd have as many as four checks depending on which bits were assigned to which component, but you'd only need two additional queues to which such tanks and ninjas could add themselves, thus saving all checks and iterating in O(N), N being the number of matches.
Again, these queues are only possible when this information is known at compile time. It's fine if the tank and ninja objects can remove these behaviors at runtime, as long as we can create the checks to manage those queues when this happens at compile time.
Knowing combinations at compile time helps in the completely dynamic case, too, as it enables you to reduce the number of checks to O(N × M/64), where M is the number of behaviors in your combination instead of the total number of behaviors.
Using queues trades checks for memory. Instead of iterating the smallest queue of objects and checking that (A) all component bits are set or (B) the entity is in all the correct queues, we just iterate the queue with the correct combination. This is good when the number of components needed in a single combination is large, but the number of necessary unique combinations is small.
In the general case, it's terrible, as the number of combinations grows exponentially in the number of behaviors. You'd want to use queues when you need to iterate all tanks which use tank logic and have tank physics and a position, and all ninjas which use ninja logic and have ninja physics and a position, but no other combinations. In this case, you'd have as many as four checks depending on which bits were assigned to which component, but you'd only need two additional queues to which such tanks and ninjas could add themselves, thus saving all checks and iterating in O(N), N being the number of matches.
Again, these queues are only possible when this information is known at compile time. It's fine if the tank and ninja objects can remove these behaviors at runtime, as long as we can create the checks to manage those queues when this happens at compile time.
Knowing combinations at compile time helps in the completely dynamic case, too, as it enables you to reduce the number of checks to O(N × M/64), where M is the number of behaviors in your combination instead of the total number of behaviors.
520
Ideas and Design / Re: ENIGMA Entity-Component-System?
« on: November 01, 2013, 12:03:26 pm »
Then you have to check all ten components; it's the same in either system. Though, I suppose if you were using a bitset, that check can be made in O(1), but with a dynamic bitset, you're back up to O(n) (specifically, n / 32 or n / 64 checks). Of course, the compile-time approach isn't incompatible with a bitset, I just dislike the notion.
Knowing at compile time which combinations of components will be required can be used to allow toolkits such as ENIGMA to have children of each notable component combination add themselves to an appropriate list. If you would like something like that, it wouldn't be hard to add a feature for it.
I had an interesting plan for the layout of the new compiler which might allow doing that for specific games without bloating ENIGMA's compiler code at all.
Knowing at compile time which combinations of components will be required can be used to allow toolkits such as ENIGMA to have children of each notable component combination add themselves to an appropriate list. If you would like something like that, it wouldn't be hard to add a feature for it.
I had an interesting plan for the layout of the new compiler which might allow doing that for specific games without bloating ENIGMA's compiler code at all.
521
Ideas and Design / Re: ENIGMA Entity-Component-System?
« on: October 28, 2013, 10:50:29 am »
> Aight, now tell me, can you come up with an algorithm that, given every entity that exists in the game world and every function like that one you show me, automatically figures out which entities can be passed to which funcions and does so one by one?
As I've stated, the simplest way to do this is to create a queue for each behavior. So, CollideableComponent contains static [snip=Java]Queue<CollideableComponent> all;[/snip], and PositionComponent contains [snip=Java]Queue<PositionComponent> all;[/snip]. In the constructor for each class (which, in C++, is called automatically for all parents), the object adds itself to the end of the queue. In the destructor, it removes itself. ENIGMA does both of these as an O(1) operation. Now, let's say you want all and only objects which are both CollideableComponent and PositionComponent. Pick the shorter of the two queues (CollideableComponent.all and PositionComponent.all) and iterate it, checking that each entity has the other component (you already know it has the first). So, this approach is O(N) in the length of the shorter list.
Another approach, which does not require RTTI, is to make the component queues keep an entity pointer, then iterate the two lists in parallel looking for identical entities. This method isn't that great, as the runtime is O(N × M); this can be reduced to, at very worst, O(M + N), using methods such as time stamps (in ENIGMA's case, entity ID). Basically, you don't have to do any backtracking because the elements were added to the queue in the order they were created. Consider these lists of entities:
We have a lot of entities with a position, and a lot which collide. For some reason, we even have entities which can collide but do not have a position (ENIGMA explicitly disallows this, as it uses tiering for these behaviors). But for the sake of argument, let's just say that's our list. We use two pointers, positioned at the start of each list, and advance like this:
So, we compare instances in this order: (1000001, 1000002), (1000004, 1000002), (1000004, 1000003), (1000004, 1000004) (yay! a match!), (1000004, 1000006), (1000005, 1000006), (1000008, 1000006), (1000001, 1000002), (1000008, 1000007) (1000008, 1000008) (another match), (1000008, 1000010), (1000009, 1000010). So, our naive little method completes in exactly M+N iterations.
Of course, in C++, you'd write template functions to handle this for you. And having ENIGMA do something like this for you would not be very complicated, either.
> You could give each Component an "active" flag or something?
That's one solution. It's kind of an ugly solution, like using a dynamic bitset. But it's still a solution, yeah. Another solution is to give each component a deactivate() method which removes itself from the queue described above, and an activate() method which re-adds itself in the correct position. I was trying to hint at this in earlier posts, but had not yet described my intentions for these queues, so I imagine it didn't make much sense.
> Also will you stop it with the boost bitset thing. That's just something an specific ECS framework implementation uses.
Yes, but it's an ugly solution that has no prettier workaround. There are numerous problems with RTTI in general which are exhibited here and rectified using a bitset. it's not as if the solution I gave above is the epitome of perfection (facing facts, it has a bilinear running time for iteration, while the ideal solution would let you iterate linearly in the number of desired elements), but I want it to be clear that there is no perfect solution. Also, the above code does not provide a solution for [snip=CSharp](inst is class)[/snip], which would probably be as ugly as dynamic_cast (which is scowled upon), or boost::dynamic_bitset (upon which I scowl).
> OK, that's cool, but you would have to loop through every entity in the world and check each and every one of them.
Only over the components in which you are interested, in O(N) in the smallest component list if you like bitsets or RTTI, or in the sum of each entity count if you do not.
> In an ECS you are just passed an iterable with all the entities that have the required components and you are ready to go.
An iterator template class which does that would be trivial to implement from either of the above methods.
As I've stated, the simplest way to do this is to create a queue for each behavior. So, CollideableComponent contains static [snip=Java]Queue<CollideableComponent> all;[/snip], and PositionComponent contains [snip=Java]Queue<PositionComponent> all;[/snip]. In the constructor for each class (which, in C++, is called automatically for all parents), the object adds itself to the end of the queue. In the destructor, it removes itself. ENIGMA does both of these as an O(1) operation. Now, let's say you want all and only objects which are both CollideableComponent and PositionComponent. Pick the shorter of the two queues (CollideableComponent.all and PositionComponent.all) and iterate it, checking that each entity has the other component (you already know it has the first). So, this approach is O(N) in the length of the shorter list.
Another approach, which does not require RTTI, is to make the component queues keep an entity pointer, then iterate the two lists in parallel looking for identical entities. This method isn't that great, as the runtime is O(N × M); this can be reduced to, at very worst, O(M + N), using methods such as time stamps (in ENIGMA's case, entity ID). Basically, you don't have to do any backtracking because the elements were added to the queue in the order they were created. Consider these lists of entities:
Code: [Select]
Collideable: 1000001 1000004 1000005 1000008 1000009
Position: 1000002 1000003 1000004 1000006 1000007 1000008 1000010
We have a lot of entities with a position, and a lot which collide. For some reason, we even have entities which can collide but do not have a position (ENIGMA explicitly disallows this, as it uses tiering for these behaviors). But for the sake of argument, let's just say that's our list. We use two pointers, positioned at the start of each list, and advance like this:
Code: (EDL) [Select]
int i = 0, j = 0;
while (i < collideable.length() && j < position.length()) {
if (collideable[i] == position[j]) {
// Tah-dah; this instance is both
}
++(collideable[i] < position[j]? i : j);
}
So, we compare instances in this order: (1000001, 1000002), (1000004, 1000002), (1000004, 1000003), (1000004, 1000004) (yay! a match!), (1000004, 1000006), (1000005, 1000006), (1000008, 1000006), (1000001, 1000002), (1000008, 1000007) (1000008, 1000008) (another match), (1000008, 1000010), (1000009, 1000010). So, our naive little method completes in exactly M+N iterations.
Of course, in C++, you'd write template functions to handle this for you. And having ENIGMA do something like this for you would not be very complicated, either.
> You could give each Component an "active" flag or something?
That's one solution. It's kind of an ugly solution, like using a dynamic bitset. But it's still a solution, yeah. Another solution is to give each component a deactivate() method which removes itself from the queue described above, and an activate() method which re-adds itself in the correct position. I was trying to hint at this in earlier posts, but had not yet described my intentions for these queues, so I imagine it didn't make much sense.
> Also will you stop it with the boost bitset thing. That's just something an specific ECS framework implementation uses.
Yes, but it's an ugly solution that has no prettier workaround. There are numerous problems with RTTI in general which are exhibited here and rectified using a bitset. it's not as if the solution I gave above is the epitome of perfection (facing facts, it has a bilinear running time for iteration, while the ideal solution would let you iterate linearly in the number of desired elements), but I want it to be clear that there is no perfect solution. Also, the above code does not provide a solution for [snip=CSharp](inst is class)[/snip], which would probably be as ugly as dynamic_cast (which is scowled upon), or boost::dynamic_bitset (upon which I scowl).
> OK, that's cool, but you would have to loop through every entity in the world and check each and every one of them.
Only over the components in which you are interested, in O(N) in the smallest component list if you like bitsets or RTTI, or in the sum of each entity count if you do not.
> In an ECS you are just passed an iterable with all the entities that have the required components and you are ready to go.
An iterator template class which does that would be trivial to implement from either of the above methods.
522
Announcements / Re: Object Inheritance Implemented
« on: October 28, 2013, 09:15:50 am »
That is because the new compiler is always one month of work into the future, and I have not had a break that long in a year and a half.
I am graduating in six weeks, five days. After that, I'm getting a job. A job will run 2.5x as many hours as my class, with the important differences being the following:
In between my graduation and my first day on the job, I will have to move. I don't know how messy a process this will be, or how much time it will leave for ENIGMA.
And also, the new compiler is, by definition, not vaporware. If any of you decided to get a clue what was going on in it, you could check out the JDI branch of ENIGMA and the new templates branch of JDI and work on it. As it stands, everyone would rather whine at me that I said it would take about a month of work, and six months later, nothing is finished.
I am graduating in six weeks, five days. After that, I'm getting a job. A job will run 2.5x as many hours as my class, with the important differences being the following:
- There is no structured set of homework assignments due over the weekend. This means that except where I choose to take work home (which is likely to happen; I'm weird when given interesting problems), my life is free outside the hours I have sold to my place of employment.
- Meetings for projects will happen inside work hours rather than absolutely whenever. This means that the hassle of corresponding with uninterested individuals on a project that no one cares about is over. I will instead be collaborating inside those previously mentioned hours with people who are actually interested in the success of the project. I don't have to pick up anyone's slack (At least, not to the degree I have been at my university).
- My workload will be managed by one company, rather than by 3-5 professors who each assume they can pile any amount of work on us at any time. This is how I end up stuck with two group projects, three individual projects, and three homework assignments, right now.
- I won't have an awkward, university-sponsored job to attend in addition to my primary focus. I spend about 1/4 the amount of time traveling to my tutoring job as I do actually participating in it.
- I'll actually get a lunch break and won't have to go hungry between the hours of 10:50 and 7:00. This doesn't help with ENIGMA so much, but is another reason I am looking forward to the event.
In between my graduation and my first day on the job, I will have to move. I don't know how messy a process this will be, or how much time it will leave for ENIGMA.
And also, the new compiler is, by definition, not vaporware. If any of you decided to get a clue what was going on in it, you could check out the JDI branch of ENIGMA and the new templates branch of JDI and work on it. As it stands, everyone would rather whine at me that I said it would take about a month of work, and six months later, nothing is finished.
523
ALLCAPS BOARD / Re: Who's idea was this?
« on: October 21, 2013, 11:03:10 am »
That song is as old as time.
http://www.youtube.com/watch?v=l12Csc_lW0Q
http://www.youtube.com/watch?v=l12Csc_lW0Q
524
Ideas and Design / Re: ENIGMA Entity-Component-System?
« on: October 21, 2013, 10:14:34 am »
The point was that his system is no more of a revolution to me than Java people constantly spouting [snip=java]if (entity instanceof SomeComponent)[/snip] or C# people doing the same with [snip]if (entity is SomeComponent)[/snip]. I get that you're attached to this idea; I don't get why you don't see the remarkable similarities between it and multiple inheritance. You're looking at the small picture of what it allows; the big picture is this:
Consider this piece of Java:
Any game entity which implements both of those required components can be passed to that function. How is this any different from your system, aside from the obvious and already-mentioned ability for me to interchange components at runtime? If that is the key difference, why is it useful? Can you give me one use case where this capability is material?
In the interest of fairness, I'll respond to you on a per-sentence basis.
> Apart from not being able to swap components (which is, for example, a perfect way of implementing a finite state machine),
Why are you using a finite state machine as an example of this? They are the perfect example of something to do at compile time. I assume you're thinking of the game's internal representations of states in terms of such a machine; for instance, in a Mario game, Mario can move between being small, being big, being fire/ice powered, being invincible, etc, etc, and so you'd just keep piling behaviors on. For that, the ability to just tack on an invincibility behavior to Mario is extremely useful. What's the big difference between doing that, and having Mario inherit from an invincible class? You aren't saving anything; you're moving a check from inside an interface to in a boost bitset. I suppose you could be saving upward of 63 bits by doing this, but I hear memory is cheap. As requested, can you give an example of where this could not be accomplished using interfaces?
> long inheritance trees are complex and messy, and couple everything together.
Let's clarify here: do you mean long inheritance trees, or do you mean deep inheritance trees? If you're complaining about the former, you and I really don't see eye to eye, and I'm much more compelled by complaints of a boost bitset and a pointer list. If you're complaining about the latter, I have good news: that shouldn't happen. See the Java code above for the use case you've been describing.
> Here, say you have a class that inherits from two other classes. How do the parents communicate with each other? They either don't, or do so in a completely ass-backwards way.
Or, they do so in the same way yours do: by having a separate function, aware of both components, which performs casting to both. See Java code.
> In an ECS you can just have two systems that require the same component.
I could very easily construct another function which depends on TankAI and TankPhysics, or TankAI and NinjaPhysics, or TankAI and NinjaHealth, which would otherwise look exactly like the above Java code except using NinjaHealth instead of TankArmor as a decision point for how to treat the AI's internal state.
> Since the same operation is run looped, threading is EASY AS FUCK. You just set some systems to run in parallel and/or to process their entities in parallel and boom, threading done.
You can do the same thing with a good number of functions similar to the one I've written above.
> Things like collision detection and resolution is also easy for much the same reasons. You just need a CollisionSystem that requires a PositionComponent and a SpatialComponent (and maybe a 'flag component' CollideableComponent, so you can have stuff with position and spatial information that doesn't collide).
Last I checked, [snip=java]if (entity instanceof CollideableComponent && entity instanceof PositionComponent)[/snip] is valid Java, provided CollideableComponent and PositionComponent are both classes. Any reason I might want to remove CollideableComponent or PositionComponent from an entity rather than remove the entity from some list, temporarily? Or give the component a boolean, instead of using a fucking bitset?
> With that, CollisionSystem has a bunch of entities with a position and spatial information that have to be rearranged so they don't collide.
What I'm demonstrating is that this use case is no different whether these components can be swapped out at runtime or not. Once again, I'm happy to hear an example where this would be useful.
In summary, the two mechanisms are arguably identical except one is done at runtime. A wise man once told me (and the rest of the class, really) that anything which can be done at compile time, probably should be done at compile time. Why can't I do all this neat shit at compile time?
Code: [Select]
Operation X
Subtype A
---Operation 1
---Operation 2
---Operation 3
Subtype B
---Operation 1
---Operation 2
---Operation 3
Subtype C
---Operation 1
---Operation 2
---Operation 3
Operation Y
Subtype A, as well
Subtype D
---Operation 1
---Operation 2
---Operation 3
Operation Z
Subtype B, as well
Subtype E
---Operation 1
---Operation 2
---Operation 3
Subtype F
---Operation 1
---Operation 2
---Operation 3
Consider this piece of Java:
Code: (java) [Select]
void tank_logic(Object entity) {
assert(entity instanceof TankArmor);
TankArmor armor = (TankArmor)entity;
assert(entity instanceof TankAI);
TankAI ai = (TankAI)entity;
MysticalChoiceList choices;
if (armor.isReallyBadAss())
choices = makeBoldChoices(ai.getSomeProperty(), ai.getSomeOtherProperty());
else
choices = makeRationalChoices(ai.getSomeProperty(), ai.getSomeOtherProperty());
// ...
}
Any game entity which implements both of those required components can be passed to that function. How is this any different from your system, aside from the obvious and already-mentioned ability for me to interchange components at runtime? If that is the key difference, why is it useful? Can you give me one use case where this capability is material?
In the interest of fairness, I'll respond to you on a per-sentence basis.
> Apart from not being able to swap components (which is, for example, a perfect way of implementing a finite state machine),
Why are you using a finite state machine as an example of this? They are the perfect example of something to do at compile time. I assume you're thinking of the game's internal representations of states in terms of such a machine; for instance, in a Mario game, Mario can move between being small, being big, being fire/ice powered, being invincible, etc, etc, and so you'd just keep piling behaviors on. For that, the ability to just tack on an invincibility behavior to Mario is extremely useful. What's the big difference between doing that, and having Mario inherit from an invincible class? You aren't saving anything; you're moving a check from inside an interface to in a boost bitset. I suppose you could be saving upward of 63 bits by doing this, but I hear memory is cheap. As requested, can you give an example of where this could not be accomplished using interfaces?
> long inheritance trees are complex and messy, and couple everything together.
Let's clarify here: do you mean long inheritance trees, or do you mean deep inheritance trees? If you're complaining about the former, you and I really don't see eye to eye, and I'm much more compelled by complaints of a boost bitset and a pointer list. If you're complaining about the latter, I have good news: that shouldn't happen. See the Java code above for the use case you've been describing.
> Here, say you have a class that inherits from two other classes. How do the parents communicate with each other? They either don't, or do so in a completely ass-backwards way.
Or, they do so in the same way yours do: by having a separate function, aware of both components, which performs casting to both. See Java code.
> In an ECS you can just have two systems that require the same component.
I could very easily construct another function which depends on TankAI and TankPhysics, or TankAI and NinjaPhysics, or TankAI and NinjaHealth, which would otherwise look exactly like the above Java code except using NinjaHealth instead of TankArmor as a decision point for how to treat the AI's internal state.
> Since the same operation is run looped, threading is EASY AS FUCK. You just set some systems to run in parallel and/or to process their entities in parallel and boom, threading done.
You can do the same thing with a good number of functions similar to the one I've written above.
> Things like collision detection and resolution is also easy for much the same reasons. You just need a CollisionSystem that requires a PositionComponent and a SpatialComponent (and maybe a 'flag component' CollideableComponent, so you can have stuff with position and spatial information that doesn't collide).
Last I checked, [snip=java]if (entity instanceof CollideableComponent && entity instanceof PositionComponent)[/snip] is valid Java, provided CollideableComponent and PositionComponent are both classes. Any reason I might want to remove CollideableComponent or PositionComponent from an entity rather than remove the entity from some list, temporarily? Or give the component a boolean, instead of using a fucking bitset?
> With that, CollisionSystem has a bunch of entities with a position and spatial information that have to be rearranged so they don't collide.
What I'm demonstrating is that this use case is no different whether these components can be swapped out at runtime or not. Once again, I'm happy to hear an example where this would be useful.
In summary, the two mechanisms are arguably identical except one is done at runtime. A wise man once told me (and the rest of the class, really) that anything which can be done at compile time, probably should be done at compile time. Why can't I do all this neat shit at compile time?
525
Ideas and Design / Re: ENIGMA Entity-Component-System?
« on: October 21, 2013, 01:16:24 am »
Indeed; I stopped reading around the time he ceased using punctuation in high frequency and just started asserting random ideas. He repeatedly states that this system is better than the "traditional inheritance hierarchy" while giving no indication that he has considered multiple inheritance/typical interface-based systems as an alternative. From what I can tell, this system is identical to multiple inheritance, except the ability to swap out components at runtime. This, he seems to handle using boost::dynamic_bitset to flag entities as having certain components. Personally, the solution makes me want to rip off my own skin, but it's cool if you're into that.
Since ENIGMA's extensions don't change out at runtime, it handles this by keeping lists of items which are known to have what you're calling "components." Instances add themselves to the proper lists on creation, and remove themselves from them upon destruction. You could do the same thing dynamically by just creating classes to handle this automatically, and simply pushing them onto some list in the instance and popping them off and deleting them when done: the ctor/dtor for those classes would take a pointer to the entity (here, instance) and handle everything for you.
That brings me back to my original question: why would you ever want to do this?
This reminds me of a lecture I had to sit through on decorator patterns. I can see this as being a fantastic and brilliant replacement to decorators. To clarify, my question at the time was, why, oh why, would you ever use a decorator?
My assessment of your ability to achieve this easily in either of Game Maker or ENIGMA has not changed. In game maker, nothing is to stop you from setting object.has_component to true, or checking if it has been set. Unfortunately, same in ENIGMA.
That said, I will look into making sure that quickly stapling attributes to objects is possible at runtime. My primary focus, however, will remain on doing so at compile time.
Since ENIGMA's extensions don't change out at runtime, it handles this by keeping lists of items which are known to have what you're calling "components." Instances add themselves to the proper lists on creation, and remove themselves from them upon destruction. You could do the same thing dynamically by just creating classes to handle this automatically, and simply pushing them onto some list in the instance and popping them off and deleting them when done: the ctor/dtor for those classes would take a pointer to the entity (here, instance) and handle everything for you.
That brings me back to my original question: why would you ever want to do this?
This reminds me of a lecture I had to sit through on decorator patterns. I can see this as being a fantastic and brilliant replacement to decorators. To clarify, my question at the time was, why, oh why, would you ever use a decorator?
My assessment of your ability to achieve this easily in either of Game Maker or ENIGMA has not changed. In game maker, nothing is to stop you from setting object.has_component to true, or checking if it has been set. Unfortunately, same in ENIGMA.
That said, I will look into making sure that quickly stapling attributes to objects is possible at runtime. My primary focus, however, will remain on doing so at compile time.
Pages: « 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 »