Two fairly straightforward approaches come to mind:
- A simple anti-overlap mechanic.
- A separation force which grows stronger the closer your enemies are. (This is in the "swarming behavior" or "steering behavior" space, if you want to learn more.)
The gist of this is that you test for your enemies actually overlapping, after computing movement, and you de-conflict them in some way. Two approaches you might consider:
- Push them apart enough to prevent overlap (can cause rippling effects),
- Give one higher priority (closest to player?) and have the other move as far as possible without colliding.
Your "track player" implementation above is often called a 'seek' behavior, essentially a force or vector that moves an agent (your enemies) towards a target (your player).
To avoid collision, you need to add some other force/vector.
The classic 'Boids' system may offer some intuition, here. Of the three rules in that link, the one you'd be implementing is called 'separation'. Generally, it means that when calculating the path an enemy takes, you check the locations of other nearby enemies, and then you calculate a force (a separate direction vector, in your case) that makes them move away from those enemies. That vector can usually be simply summed with the 'seek' vector.
I've had the most success when the force is inversely proportional to the distance they are apart - you only want enough separation force to keep them from actually running into each other.
This can be somewhat tricky to tune correctly, but it can also look a bit more believable. The main risk here is that you get two enemies trying to fit into a narrow space, preventing either from getting to the target.