In the past, most game engines used the idea of object inheritance ubiquitously. For example, animated sprites are built up from moving sprites that are built up from Base Objects. Part of this inheritance hierarchy developed because of the static nature of languages such as C++, which lend themselves to using inherited classes and virtual functions to treat different types of objects uniformly.
As game engines grew in size, people realized that a static hierarchy of classes quickly became unwieldy. Even though you might want some shallow hierarchy in your classes for where objects share the same base functionality, artificially creating a single deep hierarchy doesn’t usually make sense.
Take the example of a shooter game with a number of different weapons the player can pick up. Say you have the following three weapons:
Even in this simple case, coming up with a single hierarchy that allows for code reuse, while at the same time preventing weapons from being burdened with functionality they don’t need, is difficult. You might be tempted to create the following set of base classes:
Weapon MeleeWeapon RangedWeapon ...