Templated Game Resource Manager
Small nitpicks

AFAICT Resource::mResourceId is unused, and Resource::mResourcePath might not be necessary (in worst case, it could be queried from the ResourceManager).
For better portability, make the type of Texture::data uint8_t, as char might not be 8 bits wide on all platforms.
Inconsistent naming: sometimes it's name, sometimes it's path. Choose one and stick with it!
Also, hopefully all paths follow the same convention (e.g. no backslashes under windows, no superfluous slashes, etc.), otherwise a resource with the same physical location might be loaded for multiple different paths. Maybe introduce a way to make paths compliant to a convention (e.g. a method make_unique_path or similar).

Implementation

If a resource path is already loaded as another resource type, ResourceManager::load returns a dangling (null) reference. Maybe throw an exception instead?

Design

Are there any actual cases where Resource::unload() actually has to do anything (e.g. write the resource back)? Usually resources are read-only, so this member function might not be needed.
I would make it the Resource constructors responsibility to load the resource (and the destructors responsibility to unload it if necessary). This way, there is never an &quot;unloaded/not yet loaded&quot; state of a resource that has to be checked everywhere.
ResourceManager has a communication issue if the same resource is required by 2 or more clients: Who does the cleanup (who gets to call ResourceManager::unload(const std::string&amp;))? If done to early, every other client has a dangling reference. If not done at all, the resource will only be cleaned up when the ResourceManager is destructed - which uses memory unnecessarily in the meantime. (Careful: calling virtual functions from constructor/destructor is a bad idea!)
Why store a std::unique_ptr in ResourceManager::resources if the resource is going to be shared anyways? This is the usage std::shared_ptr and std::weak_ptr were made for!

Example:
#include &lt;memory&gt;
#include &lt;unordered_map&gt;

class Resource {
public:
    Resource(const std::string&amp;) {}
    virtual ~Resource() {}
};

class Texture : public Resource {
public:
    int width;
    int height;
    int channels;
    uint8_t* data;

    Texture(const std::string&amp; path) : Resource(path) {
        load(path);
    }

    virtual ~Texture() {
        // if needed for whatever reason
        unload();
    }
protected:
    void load(const std::string&amp; path) {
        // loading logic in here
    }
    
    void unload() {
        // unloading logic in here
    }
};

class ResourceManager {
public:
    template&lt;typename T&gt;
    std::shared_ptr&lt;T&gt; load(const std::string&amp; path) {
        static_assert(std::is_base_of&lt;Resource, T&gt;::value, &quot;T must inherit from Resource&quot;);
        
        auto res = resources[path].lock();
        if(!res) {
            // assuming constructor loads resource
            resources[path] = res = std::make_shared&lt;T&gt;(path);
        }

        auto return_value = std::dynamic_pointer_cast&lt;T&gt;(res);
        if(!return_value) {
            throw std::runtime_error(std::string(&quot;Resource '&quot;) + path + &quot;' is already loaded as another type&quot;);
        }
        return return_value;
    }
private:
    std::unordered_map&lt;std::string, std::weak_ptr&lt;Resource&gt;&gt; resources;
};

Some benefits of this design:

Resource cleanup is handled by std::shared_ptr.
Resources are unloaded as soon as they aren't needed anymore.
No dangling references!
Additional error handling if resource with the same name as another resource type is already loaded
ResourceFree isn't needed anymore, because the usual destructor already does all the work.

Other considerations

path gets passed around a lot. While I get that the high-level caller would uses that one, it might be more fitting to pass a stream (or maybe file handle / pointer to a byte array / a gsl::span) to the resource constructor. This way, the resource doesn't have to care whether it's loaded from disk, or from a zip file, or from a network stream, or from (somewhere else). However, this would actually require an ID to uniquely identify a resource (how would a path refer to a resource transmitted over the net?) - which by itself isn't a bad idea if used correctly.