Organize Classes And Components In Your Project
Organized Project Is Easier To Develop And Maintain
I have discussed previously about object inheritance, and mentioned interfaces too. Especially novice programmers and developers often think them as a complex subject, and think that you can avoid it altogether, because "I can develop a game without them". Yes, it is true - you (probably) can develop a game without them, but it would be quite messy, and very inefficient development.
All these concepts were created to make development more rational, increase the reuse of components, make maintenance much easier and give better structure for your software. This does not necessarily increase the performance of your program, but it should increase the performance of your development in many ways, because it will make things more organised. There are other factors to organize your software project - like file and directory hierarchy, indentation and comments in code etc - but I am not talking about that. This is about how your game code and assets are organised as software component hierarchies and structures with inheritance and interfaces, and as such it applies to both Blueprints and C++ in Unreal Engine.
Inheritance and Structural Types
I assume that you are already familiar with basic technical concepts of inheritance. Their implementation varies from one language to another, and some languages support features like multiple inheritance, but the principle is still the same. One class is a child class of another class (the parent class), and inherits all features (variables, functions, interfaces and other components).
This means that child has automatically all those features its parent has, unless they are specifically overridden in the child class. This makes it easy to maintain and modify your object hierarchies. If you make a change in parent class, then that change is automatically reflected in all its child classes.
I like to think that inheritance is about structure and meaning of objects. Through inheritance we start with general structure, and gradually make it more specific when we go down the hierarchy of inherited objects. Mostly it tries to define what that class is, in comparison with other classes. If you make just individual classes in a big game project, it will really hard to understand how they relate to each other, have they something common or not.
Without inheritance you will repeat the same code structures in several individual classes. When you later have to make a modification in your game logic, you have to repeat that same modification in every class that shares that mechanic. It is very probably that you forget to change in somewhere. If you had used inheritance, one change in parent class could have been sufficient.
Usually in a game there comes a situation where you have handle e.g. collisions or other mechanics, where something happens if an object is hit by a weapon, for example. There could be dozens of weapons in the game. Without inheritance you have to check individually every weapon, leading to very clumsy programming structures. Instead, if every individual weapon is a child of Weapon class, all you have to do is to check whether the object is an instance of Weapon class. In Blueprints you can do this with Cast To node
Interfaces and Functional Types
While inheritance defines the meaning of objects, interfaces define the functionality of objects. What kind of interactions they have, how they can be used. Of course every class has functions and custom events without interfaces, but interfaces are a bit more special. You can think them as something that defines important generic functional types that are meaningful in the game mechanics.
Interfaces are also something that often solves the rigid hierarchy of inheritance, and gives more flexibility to define the functionality of classes. You can use interfaces to identify totally different classes having similarity in function, although structurally they are totally different (meaning there is no direct inheritance between them).
If your game contains many items, that are instances of totally different classes, but player should be able to pick them up, then you should make an interface for that. Similarly you could define a weapon interface or several different damage interfaces. When you have implemented interfaces in your project, you can simplify your code and make it more understandable by checking if an actor implements certain interface. If it does not, then there is no need to continue execution, but if it does, then you can continue and do different castings etc.
Interface is just a list of function definitions, but the actual implementation of those functions can be different in each class. Only things that are same, are the name of the function, and its inputs and outputs. It is kind of a contract, that a class implementing an interface will have those functions available as they were defined.
Collision Definitions
Object collisions goes hand in hand with different object types, functionalities and game mechanics. Every game has some objects that can collide with other objects, then other objects that just check if they are overlapping other objects, and finally objects that do not physically interact with other objects. And usually one object may behave differently in collision depending on what it is colliding with.
In Unreal Engine project settings there are two categories - object types and trace channels. Additionally you can define collision presets. There are several default entries in each category, but usually you should define some of your own. This is an area that takes some time to learn. Putting right combination of block, overlap and ignore responses for each object type and trace channel is crucial for your game to work properly.
Remember that collision object types and trace channels are not the same as your classes. They are more like tags or labels, that you can put on actor components like static meshes. One actor may contain several components, each having a different object type label for collision.
When you have an idea of proper combinations, then it is best to make presets out of them. That way you can define all collision settings to different actor components with one setting. This makes later modifications easier. If you notice, that you have to change some of the responses, then you just modify the preset, and that change is reflected to all components using the preset.
Organize, organize, organize!
How you implement class inheritance and interfaces is totally up to you, there really is not any "one true way" for neither of them. All of it depends on the project itself. A solution that works well in one project, may not be appropriate in another. All I can say is that you should think and plan ahead. Further your project has advanced, more dangerous it comes to change those inheritances and interfaces, because any change may cause conflicts. This is something that happens so easily when you start to prototype rapidly your game, and only later try to put more order into chaos. The work could be quite overwhelming.
My personal strategy is usually as follows. First I figure out and design class inheritance hierarchy and interfaces together "on paper". When I am satisfied with the plan, first I implement interfaces, and then proceed with inheritance implementation. Then I start working out what extra object types and trace channels I may need in collision settings, and make necessary presets for different purposes. But, as usual in development, these plans change, and you will notice later, that there is need for extra object types and presets.
All that I have described above is basically about organizing your project. Good organisation is everything. Clever inheritance hierarchies and interfaces will eventually make development much easier, especially if you plan your work ahead. Structuring collision settings properly will help to keep actors behaving as they should. You can easily make project wide changes with couple of clicks. In the beginning all that may feel like "too much trouble" when you want to get fast into game development. In reality, all that planning pays in the long run. This is not about the performance optimization of your game, but all about the performance optimization of you development!