Jump to content
  • Advertisement

AthosVG

Member
  • Content count

    61
  • Joined

  • Last visited

Community Reputation

1189 Excellent

About AthosVG

  • Rank
    Member

Personal Information

  • Role
    Programmer
  • Interests
    DevOps
    Programming

Recent Profile Visitors

The recent visitors block is disabled and is not being shown to other users.

  1. A version number as a float sounds like a bad idea at best. A slight imprecission causes the thing to blow up. 0.41 can for example not be represented exactly as a float. Why not use integers? Keep an integer for the major, minor, patch etc level. Split the number at each dot and store those numbers.
  2. Effective C++ by Scott Meyers pretty a must-have in that case. It skips pretty much all the basics and has updated revisions tailored around the newest standards (11, 14 and 17)
  3. I'd expect you to create some CollisionComponent or an entity that has such a component. You have a system that iterates over the CollisionComponents (or entities) and emits particles for each component that exists. I don't see how changing this to a component adds coupling compared to using an entity. The physics system does not need to know what happens to it (in fact, it shouldn't), it just tells that a collision happened. Nothing prevents you from making something that manages the events in such a fashion that they will always be processed after a system has updated. You can delay the events, but you should also wonder why this even matters. Is it a problem that events are immediately fired off upon a collision? And be careful to not make too many theoretical situations here. It's hard to find a silver bullet immediately for handling all the cases where those events could interfere with the update of the system sending these events. So if I understand you correctly, you have a list of interactions that trigger once you figured an explosion has occurred, which you can register to? And you remove interactions as they occur? How are you adding the interaction back to that list exactly? Also, given that you have a list of interactions that are triggered after the explosion as to have to be executed, how is this different from an event that is simply called after the update? Sounds like an event to me
  4. While you can implement this an entity, why create an entity when you can also just add it as a component? I've seen approaches before where people add the events that occurred as a component to the entities involved. The problem with creating a new entity is that it somehow needs to reference the old entity. That's of course not impossible, but you could have just as well attached it to the entity right away. Regardless, the problem with having it either as an entity or component is that you need to keep track of whether it was processed. If you have two systems that are interested in knowing whether a collision has occurred, it becomes difficult to remove this Collision component/entity afterwards. None of the systems interested can assume another system that will process that entity afterwards is no longer interested in that Collision component/entity, so they cannot delete it. That means the system that added the Collision component/entity is also responsible for deleting it, otherwise the other systems may process it multiple times! Again that's something that might seem easy to fix, until you want to have your physics system update at a different rate than the other systems. Now, let's say your physics system processes collisions at 20hz and the other systems run at 60hz. Obviously, that creates a problem; now your collision components/entities persist for too long and might get processed multiple times. You can bring solutions, but in the end whatever created these event entities/components are now likely to rely upon the rate of which the systems process it. Your approach is more of a Model View Controller approach where the view (system) checks the model (the (presence of a) entity/component). It's not event based; you are likely to be creating scenarios where the events are not received. With this Model View Controller approach you are checking a state. The event of a collision having occurred should likely not be considered a state, but as it suggests, an event, i.e., a callback.
  5. It invokes the constructor of T* with the result of the call 'new T([...])'. Given that this is a pointer, the arguments of the constructor for it are either none (the pointer is uninitialized) or simply the address of a variable. No, it makes no difference in this context. Both will invoke the constructor with the matching arguments. That might not be too intuitive, given that it will call the assignment operator when the variable was already declared. Take this example: class Vector { public: Vector() = default; Vector(const Vector& inOther) { std::cout << "Copying from other vector..."; } Vector& operator=(const Vector& inOther) { std::cout << "Clearing and then copying from the other vector..."; return *this; } }; int main() { Vector vector; Vector vector2 = vector; vector2 = vector; } Which will print "Copying from other vector..." and then "Clearing and then copying from other vector...". This part is more interesting. What it does is it forwards the arguments passed the function from which this is invoked, to T. Now @Alberth mentioned this invokes the copy or move constructor, but that's not necessarily the case. The function will likely look like this: template<typename T, typename... TArguments> void CreateComponent(TArguments... inArguments) { T* component(new T(std::forward<TArguments>(inArguments)...)); } This means you can pass an arbitrary amount of template arguments to CreateComponent and these are then forwarded to the constructor of type T. This could mean a copy/move constructor is invoked, if inArguments consists of just one instance of type T or whatever else there are copy/move constructors for, but it can also be any other constructor depending on inArguments. The '...' in the template<[...], typename...[...]> means it is a parameter pack, which you can unpack by calling '...' on it again. This means the pack of 'TArguments' is expanded (unpacked) into the arguments for this function. Then, for each argument, we unpack the call to std::forward and thus forward each individual argument to it. That means each argument is forwarded from inArguments to the constructor. All std forward does is make sure that if any argument is of some reference type, it will keep being a reference once passed to the constructor of T. The && here means it's a universal reference, which is like this whole area of move semantics that makes it more confusing. What it basically means is that if I pass a reference value into this, it will retain its reference. That could either be a right-hand-value, or a left-hand-value. Both would retain their properties. This only applies to template arguments. If arguments weren't of a template type such as TArguments, this would be an rvalue-reference: a reference to a right-hand value (like again for example, the result of a function call) and would be used to distinguish between right-hand values and left-hand-values. You can generally omit the template arguments other than TransformComponent. The compiler should be able to infer this for you. This is indeed what it will end up doing. I agree on Alberth's advise, there's been quite a bit of change. Welcome to C++11
  6. AthosVG

    Unity GameObjects arrays

    You wouldn't be a first, but from my experience, the inverse tends to bite beginners more due to passing by reference being so common to them. I've had to explain the following situations a few times in the past: struct Data { int x = 0; }; class Foo { Data GetData(); } ... Foo foo; foo.GetData().x = 1; Console.WriteLine(foo.GetData().x); And the output is of course, 0. I'm honestly not a fan of it either, but in practice people tend to use the class keyword so much that it doesn't pose that much of a problem from what it seems. At that point, the default of passing it by reference ('pointer') tends to have them shoot themselves less in the foot I guess. No, there is not. The idea behind the struct keyword is that they be used for small data, while classes would be larger with explicit behaviour, making it preferable to have these passed by reference all the time. Microsoft even has guidelines on this, but ironically, they tend to not follow them consistently either.
  7. AthosVG

    Constructors with similar arguments

    Assuming I understand this correctly, you would't want it as different constructors anyway. When constructing such an object, it would be highly ambiguous anyway! I suppose your old code fixes this with having these as the functions you've presented here, which is a much better approach: calling FindX shows me that what it is calculating is X from the given parameters. As for yoru code, most has been explained already: you've got several factory functions (i.e. those functions create an instance based on the provided arguments and do require an instance of the class to call them on). That being said the comments at the start of the first constructor are very confusing An empty one can be made, but obviously only from inside the class as it's private. The second comment should probably read 'Empty constructor for constructing inside class', although I'd find 'Zero parameter constructor' or something alike a better wording. I'm not sure if you've added these for this post or if these were in code already, but either way, it's really quite redundant. In c# there's no actual notion of global functions, making static more or less the replacement. They're still functions of a class, but they do not run on an instance; in fact iirc they're not allowed to either. So you can't do class Foo { static void Bar(); } ... Foo x; x.Bar(); And have to call Foo::Bar explicitly. Code looks correct other than that, but it's more of a calculation result than a LineFormula. In all honesty, having the class be returned is already quite redundant: these functions only require you to return a single float, all the other information is what you passed into the function already
  8. Events/delegates/function pointers etc. can have a certain impact, but for a lot of scenarios the loss is more or less negligible. Using them for actual events, as 'item was added to the inventory' is an event, is usually not that big of a deal. Of course, this could be inefficient if you happen to add the hypothetical one million items to your inventory (or some other event triggering a million times in quick succession). In that case you could make it so the event is triggered less often, by for example having an array of items/data passed at once. So the OnItemAdded would become an OnItemsAdded.
  9. That should have been an 'and', so yes I agree, I was trying to point out a second benefit, my bad! Regardless, the point @Kylotan made still stands for UI. @CircleOfAwesome Also, note that this doesn't mean this approach to creating UI is necessarily bad. You don't have to have a perfect datalayout and minimal to no virtual function overhead. It's nice to have something optimal, but functionality and a decent API are likely to be your main focusses for now. Have a look into DOD if you are interested in this stuff. You can read a ton of stuff about ECS, but everyone does it differently, even though they may achieve the same, making it possibly unclear what the actual pattern entails. In the end it works towards such data-oriented design, which can be applied upon more than just entities and also more than games.
  10. No, it rarely does, hence my last part But I agree I should have emphasised this more. Kylotan gives an excellent summary of this; I mostly wanted to show an example how his code is not following tge paradigm.
  11. This looks like plain old composition to me. One of the major reasons ECS is used, or just data oriented design in general, is that in languages that support such, you can avoid having this many virtual functions, because you have a single function that operates on a batch of data. That is aside from some other major benefits, of which most relate to making it easier to tune performance. In your case with your example, assuming you are using virtual functions and inheritance/have this available, it seems you would have/need something like a virtual Draw method for the button, which then delegates drawing to its 'components', its members. Then you inherit from it, add a component and need to override this Draw method to then be called. If you have a huge amount of buttons, the overhead of these virtual calls adds up, stuff like data locality can become a problem because you are likely jumping around in memory instead of accessing it linearly. Your draw function might be super simple and lightweight, but the overhead can become significant enough to bottleneck. With a better layout and access pattern, you can improve this. In ECS terms your buttons would look more like this: struct RenderComponent { Texture Texture; } ... struct Button { RenderComponent RenderComponent; } ... Array<RenderComponent> mRenderComponents; void RenderButtons() { for (RenderComponent render_component : mRenderComponents) { Render(render_component.Texture); } } Just to give the basic idea. All rendering components are next to each other in memory, no virtual calls etc. It makes you assume you can treat the data with a single algorithm. Merely simple transformations of data. UI is somewhat more tricky in this area, as you are likely to do something different for each button being clicked, hovered and so forth. There are some overlaps where you could get some advantages, but it does not benefit as much of DOD as other areas could.
  12. You might also want to look into sprite atlases for improving performance CPU wise, although it's in a different area of the CPU overhead (sprite atlases allows the SpriteBatch to stuff more sprites into a single drawcall) and that would likely break transparency. But as in any case, if you want to improve performance, don't forget to profile first
  13. AthosVG

    Basic C# quiz

    Little off-topic, but it throws it away in case Address would be a function rather than a property. I.e. Person.GetAddress().City will compile, but throw away the result. This precisely. This can make you start a discussion much more easily and can get you to learn a lot more about the programmer. As has been said before, these are all 'spot-the-bug'-kind of questions where you either catch it or are like 'aaah' (which doesn't tell you much; he either didn't know or he knows what's going on, but just happened to oversee it). Interviews can be stressful so it becomes easier to overlook such things. Starting an actual conversation rather than turning it into an exam will make candidates feel more at ease and get the interview to line up more with their normal workflow. I get how you're interested in the interviewee's knowledge of C#, but I suggest you leave out exam questions to exams as much as possible and start conversations instead, such as by asking 'what is the difference between a reference and a value type in C#?'. For this particular example, you could, if you're really keen on it, ask the code-example question while or after discussing the topic. I know that in normal cases of problem solving you get no such hint of topic, but in this case it shows the candidate at least requires some additional knowledge to answer the question.
  14. AthosVG

    Entity-Component systems

    Not necessarily. You can work around it with using declarations (although they impose some duplication): class Light { float GetIntensity() const; } class Spotlight : private Light { public: using Light::GetIntensity; } It really depends on how you structure things. You can do this if you group all the components together, like in one big array/vector, which is something I did, rather than having each object store its own components. Upon adding/retrieving/deleting a component, you simply navigate to the correct vector containing these components (one way is to use their typeid() with std::type_index as the key for an unordered_map, just to throw in one approach). But yes, this is generally only possible if you store the components together, rather than having them be stored by the object/entity itself. Yes it was somewhat of an off-topic remark; it was related to not having the dynamic addition and removal of components altogether. On a side-note, I think that something you need to look out for is that you have already spent hours upon solving problems you haven't encountered yet. You can keep breaking your head over the perfect solution, but you don't seem to have ran into the problem yet. Have you actually tried an approach towards GameObjects that can modify behavior at runtime? The only reason I got to the way of structuring things I did was because I noticed the amount of boilerplate I was producing and managed to reduce that problem over time, by restructuring things and applying certain patterns based on the problems I was actually facing, for which I started with just plain old composition to structure my 'entities'
  15. AthosVG

    Entity-Component systems

    If you want to avoid composition and the forwarding functions it brings along, sure, but I'd prefer private inheritance if possible; you're primarily intending to substitute composition after all. Not if you don't have anything virtual, of course. You don't need a polymorphic Component class, if you need that Component class at all. You can work around this with templates, considering you seem to be wanting to make the adding, removing etc. of components very generic. Else you could of course just stick with plain old composition.
  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

Participate in the game development conversation and more when you create an account on GameDev.net!

Sign me up!