skylarbpayne / component-entity-system Goto Github PK
View Code? Open in Web Editor NEWLicense: Apache License 2.0
License: Apache License 2.0
The EntityFactory should be able to take a configuration file (in Lua) and construct an Entity from it. Lua entity scripts will be in a format like:
Entity =
{
Render =
{
...Render component data
}
...Other components
}
The EntityFactory should have a Create method that returns an Entity and takes a lua file as a parameter.
For the EntityFactory to not have to be constantly changed with each component addition, Further components will have to implement a Load function (not a member method) which will take a Lua state and load it's own data from the lua state: IComponent* Load(lua_State* L)
The scene that instantiates the EntityFactory should register all component types to the hash table in the EntityFactory. This Hash table will have keys representing the component type (which should be the same name used in the Lua config file), and the associated value will be a function pointer (likely wrapped in the nice std::function).
The Create function will work something like this:
Entity* e = new Entity();
luaState* L = lua_newstate();
lua_getglobal(L, "Entity");
lua_pushnil(L);
while(lua_next(L, 1) != 0)
{
auto function = functions[lua_tostring(L, -2)];
if(!function) error
IComponent* c = function(L);
e->AttachComponent(c);
lua_pop(L, 1);
}
lua_close(L);
AddEntity(e);
And the load function for components will look something like:
ComponentType* c = new ComponentType();
lua_pushnil(L);
while(lua_next(L, -2) != 0)
{
//table is at index -3... Remember, table of tables!
lua_pushstring("variable_name");
lua_gettable(L, -3); //this should get table[variable_name]
variable_name = lua_tonumber(L, -1); // <-- this is your value
...
}
return c;
When RenderSystem receives an entity moved message, it has to move the graphical representation of that entity.
System::Update accepts an unsigned int that is supposed to be interpreted as microseconds. However, some systems may want to use millseconds or seconds. Passing an sf::Time object instead (which is returned from sf::getElapsedTime anyway) will make the code easier to use and also provide greater flexibility.
The "Unload" method in the IResourceCache interface is not purely virtual and all subsequent derived classes do not implement it currently.
Due to this, the resource caches are never cleared during scene changes.
Simply mark the Unload method as pure virtual, and implement it in all deriving classes to fix it. (There may even be an easier general way to do it within the IResourceCache Unload method)
RenderComponent.h: Use sf::Color const& instead of sf::Color to avoid copying.
It's unnecessary to have a BehaviorManager, it can work just as well, perhaps better, under the SystemManaager
Every other frame the ID is wrong. Perhaps allow each scriptable behavior to run on its own thread? that way globals won't change as often and only a single load call is needed.
With the new types of render components, RenderSystem needs to be adjusted.
In SpriteComponent pass a reference to texture in the SetTexture reference
Not sure why yet, must investigate further.
Edit: ColliderComponent uses an Offset from the position, and I used the position. So when the collision check was running, it was essentially using position twice.
Since the scene is instantiated with the creation of a ChangeSceneMessage, at that point the necessary data should be appropriately set.
Using SFML, return both the x and y coordinates of the mouse
The time slice (dt) passed to all Systems update methods is the current time rather than the actual time splice because the Last time variable is never updated.
A simple LastTime = CurrentTime will fix it.
When you use the delete operator on a pointer to IRenderComponent (which you cannot actually have an object of, so it must be pointing to an object of a class that derives from IRenderComponent) the destructors will only be properly called if the IRenderComponent destructor is virtual.
Add a collision check for newly created entities (if they have a collision component)
RenderComponent.h: GetCircle method uses copy constructor. Use pointer or reference (reference preferred).
IComponent.h: Add a pure virtual Load method for EntityFactory to call.
Add a create entity message with a lua binding
Each render component needs a load method to load in important data from Lua.
Write a new SpriteComponent that uses the IRenderComponent interface. Make sure to have some general accessors.
Take a look at http://sfml-dev.org/documentation/2.0/classsf_1_1Text.php
When an entity is created on top of another entity, the CollisionSystem tries to resolve the collision. However, if we have a situation like:
xxx
xxx and you try to place an entity on top of the center x and the lease significant
x x overlap is downward, then entity will be pushed down. But this causes another collision with entities on the right and left, and since the collision only occurs between right and left, the entity is continuously pushed back and forth and the entity becomes stuck and the game freezes.
EntityFactory.cpp: When calling lua_getglobal(L, "Behaviors), check to see that the global was received. Should probably do that for components as well.
More important for behavior because all entities will have components, but not all entities will have behaviors.
In TextComponent and SpriteComponent, pass strings as std::string const&. There's no reason to copy the string, so it's fine to just pass a reference to it.
Crashes when GetEntity is called.
If you have an inheritance chain like: A->B->C (such that C inherits from B inherits from A), then when you instantiate an object of C, A's constructor is called first, then B's, and then C. When calling a super classes constructor in an initializer list, the point is to pass certain parameters because an initializer list is really the only way to do it. When you call the default constructor in an initializer list, it's very redundant.
So in CircleComponent, RectangleComponent, SpriteComponent, and TextComponent, that should be fixed ;).
And if you notice it anywhere else, you can fix it :p
Both file comments reference the wrong file.
Sometimes, the window dimensions may be important for the purposes of a script.
Especially useful for lua scripts where it's necessary to know what type of entity that you're dealing with.
I plan to simply implement tags as something that every entity has. By default the tag will be "". The tag should have get and set properties, as well as the ability to pass the tag into the entity constructor.
This will allow tags to define classes of entities (such as "enemy", "friendly", etc) or individual entities (such as "p1score" or "p2score").
In the same way that you made the sprite, text, and circle components, make a rectangle component.
PositionComponent.h: The SetPosition method uses ints, but it should use floats.
Have both the Text and Sprite components inherit from ResourceAccessor so that they can get their own assets through string. This will allow loading to remain largely unchanged.
The revamp will happen in 4 steps:
Abstract the collision detection loop into a method to make the fix of issue #49 easier, and also the 4th step easier.
fix #49 by checking for collisions when an entity is validated. If it's colliding with something on creation, kill it.
Use a list over a set for moved entities. And when new entities are added, use push_front. The collision resolution should happen in the opposite order that entities were removed.
Implement "bins". Customization of the number and size of partitions should be easy. Each bin should contain a set of IDs (which will be the IDs of the entities that exist in that bin). When an Entity moves, check to see which bin it belongs in, and move it to that bin (if it passed through it's previous bin). If the entity cannot fit in a single bin, it is okay to put it in multiple bins! Have a check that takes a collider component and determines which bin(s) the entity belongs in. So now when an entity moved message flashes, find the bin(s) that it belongs to and insert it into that/those bin(s) and remove it from any bins that it no longer belongs to. Lastly, when checking for collisions, check only against entities from bins that the entity belongs to.
Since we now have multiple types of render components, it no longer makes sense for that to be called RenderComponent
RenderComponent.h: Prototype for GetShape should be:
sf::Circle const& RenderComponent::GetShape() const
Make sure the number of items on the stack is equivalent to the number of parameters. If there's a discrepancy, or any other problem occurs, push an error string onto the stack and remember, the return value of the function bound to lua is the number of return values (ie the number of items on the stack after the function returns).
Add fields into both of those lua configurations to allow the configuration of r, g, b, and a values of rectangles and circles.
Write a TextComponent that inherits from the IRenderComponent interface. Again, general accessors.
As of now, the Particle System will contain of three main classes:
The Particle, ParticleComponent, and ParticleSystem.
The ParticleComponent and ParticleSystem will work in tandem in a similar fashion to the other Components / Systems you've made thus far. The ParticleComponent will define data that the ParticleSystem will use to instantiate, and show, components.
The ParticleSystem will be much more complicated than other systems, however. At first it may seem that you could treat every particle as an entity and let the systems that are already in place update and render the particles. You could do that, but each Particle Emitter (basically every time you use a ParticleComponent) will typically emit thousands of particles. This will unnecessarily clog up the entity system since particles are short lived. This would put a great deal of unnecessary stress and computation where it does not need to be.
Instead, the ParticleSystem will be responsible for instantiating, updating, rendering, and destroying every particle.
So let's break this down a bit. The ParticleComponent will need:
Using this information, the ParticleSystem will Instantiate new particles.
Particles should have the following data:
For now we will only worry about using sf::Point, but later we will extend the implementation to use textured quads to create better effects.
From here, you can more or less use the SFML model as a guide. Just keep in mind that you aren't making a class that inherits from drawable/transformable.
the Update method in the particle system should do everything the update method in the SFML version does as well as the rendering.
If you have any questions, let me know.
Before placing an entity, check for collisions to see if it CAN be placed there. That way a spiral of death collision detection is avoided. If something is already there, don't place it.
The RenderNode component will allow entities to have multiple rendering type components. The Component will inherit from IRenderComponent, but the important part of this is creating the RenderNode (I call it that for lack of a better name).
class RenderNode : public sf::Drawable, public sf::Transformable
Inheriting from these two classes will allow a simple call to window.draw(node) to render all drawables held in the node. Also, by providing a transform, all drawables can simply store an "offset" transform (basically define a transform local to the node's transform).
Which means the draw call will look like:
draw(sf::RenderTarget& target, sf::RenderStates states)
{
//This applies the main transform
states.transform _= getTransform();
for(i = 0; i < numDrawables; i++)
{
//In each successive draw call, the drawable will add it's own transform on (if applicable)
target.draw(_drawables[i], states);
}
}
For Lua loading, a recursive table should work.
However, before implementation, consider how useful this will actually be. Is there any real benefit?
Consider all the types of Drawables you might have:
*Sprite
*Shape
*Text
*Particles
So let's say you have a complex entity that has a Sprite, with it's name in text rendered above it, a health bar rendered nearby, and particles rendered all around.
The point of this RenderNode is to make things easier when priority rendering is implemented (have to make sure entities are rendered in the correct order or you get weird effects). The health bar and name should definitely be rendered over any sprites (it would be strange if a character could "walk over" your name or health bar). So adding them into the same priority queue with other sprites is pointless. It's much more efficient to group like things together. The particles probably should be rendered above everything else as well.
While having one central rendering point may seem nice, it may not be in the long run.
It might then be better to have (for this specific example):
RenderSystem (for the representation of the entity)
ParticleSystem(for any effects from the entity)
HealthSystem(renders health of the entity)
NameSystem(renders name of the entity)
where each System adds it's drawables to a rendering queue, and then renders them. Less drawables to sort through each time = faster rendering.
The Particles, Health, and Name are all rendering data about the entity. While the RenderSystem should render things that represent the entity itself.
Thus, in typing this whole spiel out, I have decided against implementing this feature. However, I will at some point implement a better rendering routine that will rearrange the order.
Sometimes it may be necessary to delay (sleep/wait) for a certain amount of time.
Now we're going to have multiple rendering sources (various shapes like circles and rectangles, sprites, and text) we want to have a render component interface (IRenderComponent).
All rendering components will inherit from IRenderComponent once it is complete.
IRenderComponent should have a constructor exactly like the one in the current circle RenderComponent. Aside from that, it should have a pure virtual "GetDrawable" method. this way a single render system can be used to render any rendering components.
Once the IRenderComponent is complete, modify the current circle RenderComponent to inherit from IRenderComponent.
Note: You'll have to slightly modify RenderSystem to work with this. All components inheriting from IRenderComponent will have type "Render", so the ValidateEntity method should be fine. However, the Update method will need to call GetComponent with the template parameter of IRenderComponent, and regular parameter of "Render". Then call the pure virtual GetDrawable method mentioned earlier to get the Drawable.
When an entity with a lower ID number (0 compared to 1) moves into an entity with a higher ID, the latter entity is "pushed". However, reverse the situation and the entity will not budge.
RenderComponent.h: Instead of only having a GetCircle method, write methods to get and set the color and radius.
IResourceCache has a stored directory string that it does not use. Put it in use so that you can call GetTexture("spaceship.png") and it will internally use "resources/spaceship.png".
In SpriteComponent, you had the SetColor method accept an unused string.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.