I think knowing why static classes are sometimes frowned upon is key. A lot of it has to do with avoiding singletons. I think Vryken’s idea is not a bad solution to your specific question. It doesn’t get you out of all the problems in the singleton pattern, but it’s better than what you have.
Singletons are avoided because they can make your code less flexible. Singleton problems apply both when having a static player reference, or when getting the player by tag in the way that you suggested: you still expect to have only one object of a specific type at a time.
In your case, if you want to test an entity in isolation, you have to create a player, and if your player needs other elements to work, you have to create them too. The chain of dependencies that are needed for a single thing to work can get huge. Suddenly, to find out why an enemy isn’t shooting in the right direction, you need to create UI, health bars, inventories, a level manager, etc. And if you have multiple bugs, it’ll be harder to find them, because it’ll be harder to make just one of them happen. A long chain of dependencies by itself makes it easier for you to introduce bugs.
Singletons also tend to create dependencies on specific quantities of specific classes. For example, they make it much harder to add local multiplayer, or to make some entities target something that’s not a player.
The thing is, these problems aren’t too bad for simple stuff or prototypes. Even for complex stuff, singletons can be okay when used sparingly. It depends a lot on your use case and the nature of your project; for most beginners with small projects, I’d recommend not worrying about it a lot.
That said, the main alternative to singletons is having a way to assign dependencies to objects directly. It could be a field in the editor (my favorite), a method with parameters, a way for searching for things near, some dependency injection system like Extenject, etc. This way, you can easily change things like an enemy’s target. If you want, you can still have the singleton as a backup, in case a dependency isn’t assigned in other way.
Another thing that helps is to reference abstract classes or interfaces instead of concrete types. For example, it allows you to make target.position an abstract property, then you can have different targets that calculate their position in different ways.