Step 1: classes are structs with functions. Mixing data with functions is useful.
Step 2: In a class you limit the visibility of functions and variables. Usually you will have a few public functions that are the "interface" of the class. The advantage of hiding things is you can change them without the user's of your class needing to change. As long as you preserve the public interface you are good to go. It also makes it easier for people to use your class as they won't get confused trying to understand the private details and can focus on the public interface.
Step 3: You can inherit fields and functions from a parent class. This can help prevent some duplication between different sub-types. You can also override the functionality of inherited functions if you choose. Inheritance is one of the least useful features of OOP but there it is.
Step 4: Polymorphism lets you create "pluggable" software. To add functionality you make a new class that satisfies an interface with it's functions. You create the class and it sort of plugs in. You do not make any changes to the work-flow code. Without OOP you would have lots of "if-elseif-elseif" statements in your work-flow to perform different actions for different things. With OOP those if statements disappear because you call the method of your object. Your object's function does what it needs to do polymorphically.
For example you have a class "Engine". It has children "DirectXEngine", "OpenGLEngine". Your game works fine for years. Then 5 years later someone events a new engine and you want to support it in your game. You create a class "SomeNewEngine" and it plugs into your video game. You fully support the new engine without any changes to the workflow code. All you had to do was make a new class. All you have to test (in theory) is that your new class's functions satisfy the interface. (but in reality it is not always feasible to conform things to the same interface...)
Step 5: Understanding polymorphism is not limited to OOP. You can get polymorphic behavior from generic programming. Consider an "add" function
T add(T val1, T val2) {
return val1 + val2;
}
It works whether you pass in floats, ints, doubles, strings, etc. Even though the implementation of adding these types is very different. This is polymorphism on the source code level. If you think of the "+" operator as a behavior that all the types support, then they all conform the the "+" interface.
You can conceptualize all behavior as functions. Whether it's an actual function or a syntax of the language like +, -, %, etc.
Take it a step further and you remove all syntax from the language. You are now at the point every function you create "is" the language. You create your own language as you program in your language. You support all programming paradigms but are no paradigm. OOP becomes an irrelevant definition of a few techniques that are part of the natural world.