Mike2343

Members
  • Content count

    1311
  • Joined

  • Last visited

  • Days Won

    2

Mike2343 last won the day on December 5

Mike2343 had the most liked content!

Community Reputation

1229 Excellent

2 Followers

About Mike2343

  • Rank
    Contributor

Personal Information

  • Interests
    Programming
  1. Hence my usage of the word "prefer". There are exceptions to every rule but for the most part I try and use descriptive names. Edge cases are edge cases and I will put // Usage: notes. I've just experienced enough incidents where the internal code was changed (header not touched) and the documentation was not updated and apparent code review didn't catch it either. It can still happen with how I do things too, but if your incoming parameter is "const float meters_per_sec" I just find it's a bit less likely to be turned into something else as easily.
  2. What language did you use btw, I've never seen it before.
  3. Pointers and defragmentation

    This is exactly why my components don't have any knowledge of which entity they're attached to. Only the component system has that information. It can return a handle (when a component is created) into the front facing buffer (public side of the system) which can then if it needs it, have a second buffer which holds the component pointer. using Handle = uint32_t; struct TransformComponent { glm::mat4 m_Transform; } class TransformSystem { public: //... Handle CreateComponent( const Entity& parent_entity, ... ); private: // m_CurrentHandle can just get ++'d in a simple system, or could be more complex reusing freed handles, etc... Handle m_CurrentHandle = 0; std::map<Handle, std::size_t> m_TranslationMap; // This Handle is the parent_entity ID from the CreateComponent for example. Just using map as an example. std::map<Handle, TransformComponent*> m_Components; }; Now if we defragment m_Components we just have to update the m_TranslationMap's std::size_t value to point to the new location. The handle provided from CreateComponent() never needs to change.
  4. I've never really seen the appeal of doxygen honestly. I prefer to use descriptive function names and parameters and proper use of const to indicate the functions intentions. I find it faster to write code this way and more maintainable. Obviously YMMV.
  5. Entity-Component systems

    That's pretty darn smart. I'll give it a try, thanks
  6. Having just watched this video, I think it would apply well to the discussion: http://www.gdcvault.com/play/1022186/Parallelizing-the-Naughty-Dog-Engine More so the last half of the video on their multithreading, multi frame system. But honestly, you should try and go as wide as possible. Do all physics, all animation, moving on after each stage is done.
  7. Entity-Component systems

    class CameraSystem { struct LenseData { uint64_t m_LenseID; // for PBR cameras }; struct CameraData { uint64_t m_CameraID; // hashed name provided to CreateCamera() LenseData *m_Lense = nullptr; // ... all the data for any type of camera kept here, near/far plane, viewport info (height/width), aspect ratio, etc. }; public: // ... uint64_t CreateCamera( const std::string camera_name, const glm::vec3& position, ... ); uint64_t CreateLense( const std::string lense_name, ... ); void AttachLense( const uint64_t& camera, const uint64_t& lense ); void Position( const uint64_t& camera, const glm::vec3& new_position ); glm::vec3 Position( const uint64_t& camera ) const; // A lot more getters and setters... private: friend Renderer; friend CullingSystem; glm::mat4 ProjectionMatrix( const uint64_t& camera ) const; glm::mat4 OrthoMatrix( const uint64_t& camera ) const; std::vector<CameraData*> m_Cameras; std::vector<LenseData*> m_Lenses; }; As you can see, it's a bit of a mess right now, I just kept adding functionality to it. Forgot to add in the BuildBuffer() function that is in the private section for the renderer too. It builds the UniformBufferObject (OpenGL backend) that gets uploaded to any shader requiring camera data. Right now the renderer actually buffers the "main camera" data as most things need it, instead of calling into this function each time. Only if the shader or rendering requires a different camera for say shadow maps does it then come back to this class and gets that specific cameras buffers and transforms, etc.
  8. Entity-Component systems

    Much like I showed in my example code but it only returns a hash of it's name as reference; each camera also has to have a name which just makes it easier to search for hash("main camera"). The culling system is also a friend of the camera class, so it can do plane vs sphere then plane vs OBB/AABB culling tests. I'm using OpenGL currently and yes the camera class just sets a uniform buffer which the renderer passes on to the shader. The camera class is more or less a dumb class, just holding data and builds basic transforms glm::mat4 Projection() const; type stuff.
  9. Entity-Component systems

    Yeah, right now it's all just stored in a struct within the camera system. I likely need to revise it, but for now it just works. I also don't plan on supporting VR, so not to worried about it. Again, I'm going as generic as possible where needed. But not adding anymore features then I currently need.
  10. Entity-Component systems

    I do too. Each light is rendered Doom 2016 style when it needs updating. My main camera is a PBR implementation also which can have different lenses, etc. I just don't think it needs to be a component. Instead the vector is kept in the camera system and changes need to be made through it's interface. The Renderer is a friend though so it can directly access the camera data for rendering.
  11. Entity-Component systems

    I personally wouldn't make a Camera component but I just posted that But for two or three items I just do if statements personally, which is just the same thing, I just feel they look and read better than switch/case statements. But I do not see where I would need a union of all data of all types? Again though I don't feel like a Camera is a low enough level item to be a component, though I'm still debating on that... Lights are another complex one, I do agree. I do tend to try and share as many variables as possible to minimize how many structs I have (and I don't use unions much honestly, just typically find a way around them but I'm starting to see their appeal more and more). I'm a huge fan of the KISS principle. I'm not trying to make a generic reusable engine I can share with the world, but for the specific game we're focused on making now (dungeon crawler). I just look at the data I have or am going to have and how I need to translate it. I do my best to keep it tightly packed and something I can go wide over to utilize multithreading as much as possible. Why I like the ECS (name I use, as there really is no definite definition of it).
  12. Entity-Component systems

    Yes, for scripts it would be. Though, unless you wrote a custom memory allocator for unique_ptr it could still be large jumps in memory whereas searching over the list in my setup would be far more cache friendly. I also don't think everything needs to be a component either. I hadn't really planned on making Camera's into components, instead my plan was to have a small follow struct in my camera class that kept a handle to the TransformComponent, and a quaternion of it's target or offset (for 3rd person or say a security camera).
  13. Entity-Component systems

    I am going to use LUA (note I don't have scripting implemented yet as I'm not at that point), I was trying to be as generic as possible as I'm not sure what scripting language/system you're using. But scripts aren't really something I would worry about the cache coherency of, as they are unlikely to be run/updated in order. My plan was to load the LUA script, compile it JIT and store the result of the compile in the script component for easy access, hence the m_ScriptID in my mind. I wrote up the example more to show you don't need a 1000 individual ScriptComponentType1, *Type2, ... that you could likely get away with one or two (say LUAScriptComponent and PythonScriptComponent along with their respective systems) and just attach near infinite scripts to a single entity. My goal is to keep my structs small enough if possible to fit in a single cache line whenever possible. And for most components that should be very easy (think Position) or into SIMD lanes (transforms).
  14. Entity-Component systems

    struct ScriptComponent { std::string m_TextData; std::string m_ScriptName; uint64_t m_ScriptID; // Either a hashed m_ScriptName, or provided by the ScriptSystem when it's compiled. uint64_t m_Parent; bool m_IsCompiled; // Might not even be needed, could use the m_ScriptID too maybe. }; // ... If it needs to be shared amoung systems add it to the DataFolder as another vector for easy sharing class ScriptSystem { public: ScriptSystem( const DataFolder& data_folder, const std::size_t& memory_size, void* memory_start ); ScriptComponent* CreateScript( Entity& entity, const std::string script_name, const bool auto_compile = true ); std::vector<ScriptComponent*> FindScripts( const uint64_t& entity_ID ); bool ExecuteScript( const uint64_t& entity_ID, const uint64_t& script_id, void *user_data ); private: std::vector<std::size_t> m_FreedSlotList; // Takes from here first when CreateScript() is called. }; Not each script will be it's own component. ScriptComponent would just take up one enum out of those 64 and you can increase that value to well over 256. But if you have 256 components... that's a lot of very low level things. I'm also planning on splitting up game logic and rendering components. The only shared component being a RenderableComponent that holds a uint64_t ID of the entity on the graphics side of the wall. I like to keep my game logic and engine as separate as possible. As I just showed, the system would just return a vector, or array or whatever with all the components for that entity. You just flip the bit saying it has at least one. I would also add to the Create function another variable of how many you want allocated for that entity so they can be side by side in memory. I don't have any inheritance in this system, no base classes are needed. My components are pure data. enum class CameraType: uint8_t { Orthographic, Persepective }; struct CameraComponent { uint64_t m_ParentID; float m_FOV; float m_NearPlane; float m_FarPlane; // ... CameraType m_CameraType; }; class CameraSystem { public: // ... CameraComponent* CreateCamera(...); void SetFOV( const uint64_t& entity_id, const float fov ); float GetFOV( const uint64_t& entity_id ) const; // ... carry on with setters/getters, I don't use the word set/get as I find it reads easier but to each their own. }; You could also just Find() the component pointer and modify it's values directly too. I use handles though but they're a more complex topic and were off topic so I went with pure pointers. I'm not sure what you mean? Only thing changed is that the getter and setters are now in a seperate class meant to handle all the logic related to the data. I'm sure this could be added to the class too. It doesn't. For that, I use handles and two lists with indirection. The handle stores an index into the first list, the first list maps that index into the currently location in the second, real list (that the system works on) so you can rearrange the lists to be truly continuous in memory. But that was beyond my little write up. A couple holes won't completely kill the cache if you have 1000 transforms being worked on for example. It just won't be completely as optimized as it could be. I was more trying to show the struct holding the shared vectors for easy access and my bitset idea.
  15. Entity-Component systems

    No, I was thinking of having a linked list or something similar and having the CreateTransform() look into this first before taking from it first. It's nice, because all TransformComponents are always the same size, so it could just be an array of offsets: return m_MemoryStart + (offset * sizeof(TransformComponent));