Sunday, 23 January 2022

Unity How To implement Factory Pattern

Factory Pattern is very popular if you are looking for creating kind of same objects with different properties for example implementing additional properties to factorized game object like adding different colors abilities

Most important step is to create a good factory class which can be implemented in to most different  situations I prefer a static class with generic option because there can be different  objects it completely isolates from other classes.

One certain property is imprtant in our example its "Name" property name of prefab or game object to create from factory

In Our example thjere are different type of buildings "House","Office","Government" buildings they both have same properties and some of them may change.


PackageLink is here  ClickTo Download


I started with a building FactoryGenerator Class

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using UnityEngine;

public static class FactoryGenerator<T> where T : class
{
    
    // Name of prefab or game object
    private const string NAME = "Name";
    // dictionary for keeping created factories and find by name when its needed
    private static Dictionary<string, T> factoryTypesByName;
    // check if system started by a simple null check
    private static bool isinitialized = factoryTypesByName != null;
    public static void InitializeFactoryGenerator()
    {
        
        // if started no need to work code again
        if (isinitialized) { return; }
        // creawte instance of dictionary to add
        factoryTypesByName = new Dictionary<string, T>();
        // get all the factories which arent abstract and child of our main class provided with (T)  
        // this line uses System.Linq
        var factoryTypes = Assembly.GetAssembly(typeof(T)).GetTypes().Where(x => x.IsClass && !x.IsAbstract && x.IsSubclassOf((typeof(T))));
       
        foreach (var type in factoryTypes)
        {
            //Creates instance of subclass
            var factory = Activator.CreateInstance(type) as T;
            // get Name property of class as string 
            var rslt = factory.GetType().GetProperty(NAME).GetValue(factory) as string;
            // add to Dictionary
            factoryTypesByName.Add(rslt, factory);
        }
    }
    /// 
    /// calls from factory by PrefabName
    /// 
    ///
    /// 
    public static T CreateObject(string Code)
    {
        // get first macth of class and return as class
        return factoryTypesByName.Where(v => v.Key == Code).Select(x => x.Value).FirstOrDefault();
    }
}


  
=> And Main FactoryClass as a base class to other factory classes
*** This class has one rule has to have a "Name" property to provide name of Prefab
=> We may add different virtual methods for base operations and override some of them to improve  since it's a simple example I have added only a few  fields

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public abstract class Buildings:MonoBehaviour
{
    public abstract string Name { get; }
    // property of a building
    public abstract Color buildingColor { get; }
    // sample creation of building
    public virtual void CreateBuilding()
    {
    /// creates object based on name
        GameObject gmObj = Resources.Load(this.Name) as GameObject;
        Instantiate(gmObj);
        gmObj.transform.position = Vector3.zero;
    }
}



those two classes are the main structure for factory pattern now its time to create different types of factorized objects all the classes will be the same but can be changed easily and separately all of them are Inherited from the "Buildings" Class

=> Office Class

public class Office : Buildings
{
    public override string Name => "Office";

    public override Color buildingColor => Color.red;

    public override void CreateBuilding()
    {
        base.CreateBuilding();
    }
}

=>Hause Class

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class House : Buildings
{
    public override string Name => "House";

    public override Color buildingColor => Color.green;

    public override void CreateBuilding()
    {
        base.CreateBuilding();
    }
}
=> Government Class
using System.Collections;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Government : Buildings
{
    public override string Name => "Government";

    public override Color buildingColor => Color.blue;

    public override void CreateBuilding()
    {
        base.CreateBuilding();
       
    }
}
=> Finally we havve all structure we need now its time to call from factory
Create a simple scene and create 3 prefabs in "Resources" Folder Make sure prefabname is same as property "Name" Than add 3 Buttons to your Scene like this 
And Add An Empty Game object Add "CreateFromFactory" script to this object

Assign Buttons one by one 
HouseButton=>CreateHause
OfficeButton=>CreateOffice
GovernmentButton=>CreateGovernment

=>"CreateFromFactory" Class is  should be like below
calls the factory by object name and creates game objects

public class CreateFromFactory : MonoBehaviour
{
    private void Start()
    {
        // setup building factory;
        FactoryGenerator<Buildings>.InitializeFactoryGenerator();
    }

    public void CreateOffice()
    {
        this.CreateFactory("Office");
    }
    public void CreateHause()
    {
        this.CreateFactory("House");
    }
    public void CreateGovernment()
    {
        this.CreateFactory("Government");
    }


    private void CreateFactory(string name)
    {
        Buildings rslt = FactoryGenerator<Buildings>.CreateObject(name);
        rslt.CreateBuilding();
    }

}
=>Result
Its normal you may think like why arent be just instantiating objects yes it can be made but 
to manage every object in game and gaining control over properties e specially when you have  same object with different proprerties this approach cames in handy 
and keeps your much cleaner and readable  every time
Also improves reuseablity of code with different projects.


Happy Coding 





No comments:

Post a Comment