简体   繁体   中英

Unity set Gameobject based on parameter 'Type'

I want to create a strategy game. When I click the "BuildHouse" button in my action bar I want my manager to register the building that should be built.

All my buildings are components so I try to pass in the building type as a parameter.

My button executes

public void BuildFarm()
{
    buildManager.SetBuilding(typeof(Farm)); // Farm is a child of Building
}

Now the BuildManager knows the type of the building. I tried to iterate through all the building prefabs and pick the one that matches the type.

public class BuildManager : MonoBehaviour
{
    [SerializeField]
    private GameObject[] buildings;

    public void SetBuilding(Type buildingType)
    {
        GameObject targetBuilding = buildings.Where(currentBuilding => buildingType == currentBuilding.GetComponent<Building>().GetType()).First();
    }
}

I don't know if there is a better way to make the BuildManager get to know what to build. If this idea is bad at all please provide a better way of implementation.

In my opinion filtering by component type is absolutely fine. If you have Farm and House components and they are both buildings, it's natural to express it with an inheritance hierarchy.

As concerns using enum : enums are best at representing simple state or well-specified values that are unlikely to change, eg days of week . There will be no 8th day of week, right? On the other hand, you may want to freely extend the list of available buildings, and each building may implement different game logic. A more natural way to express that is to create a class hierarchy. And when the classes are in place, adding an enum for the same thing is superfluous.

That said, I'd suggest some improvements in your code.

    public void SetBuilding<TBuilding>() where TBuilding : IBuilding
    {
        GameObject targetBuilding = buildings
           .Where(currentBuilding => 
              typeof(TBuilding) == currentBuilding.GetComponent<Building>().GetType())
           .SingleOrDefault();

        if(targetBuilding == null)
        {
           // throw or log an error here
        }
        else
        {
           // instantiate
        }
    }
  1. If buildingType is always known at compile-time you can use generics. It makes calls to this method slightly more readable: buildManager.SetBuilding<Farm>();

  2. Notice the where TBuilding : IBuilding constraint. Make your Farm and House classes implement a common interface IBuilding (it could be a common base class too): class Farm : Component, IBuilding . By this compiler ensures you can't call something like buildManager.SetBuilding<int>();

  3. SingleOrDefault ensures there's exactly one such GO. In case there's none or more than 1, it returns null. If that's the case, throw or log an error. I know you are making sure the buildings array is correct but you should enforce it with code.

I suggest defining an enum :

enum BuildingType
{
    Farm,
    House,
    ...
}

Then you can define a Type property for Building :

public class Building
{
    ...
    BuildingType Type { get; set; }
    ... 
}

Then you can call SetBuilding like this (maybe use a switch ):

public void SetBuilding(BuildingType buildingType)
{
    // act according to buildingType
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM