Customizable Unit

Hello all,

I am working on a prototype for a turn-based strategy (sort of) game where you are controlling a unit (I still haven’t decided what this unit is going to be but, for now, let’s pretend it is a tank).

My design idea is such that as the player accumulates experience, an array of mountable parts will be made available to the player. Some examples of the mountable parts would be stronger turret, powerful gun, stronger engine, and etc. There will also be an array of tanks that can be acquired. So, the aim is to allow the player to mix and match various tanks with multiple parts.

Having said that, I have done quite a bit of research on this topic but found nothing that is even remotely similar. Will I have to actually buy some assets to accomplish something like this? So far, I have been able to create a number of units spawn on a small map, shoot projectiles at each other. I have no clue where to start as to how these units can move around with mounted parts (each mounted part should have a distinct visual representation). I realize I will have to first construct these various parts in 3D graphics package - that’s not a problem. But incorporating them into the game mechanics is where I am feeling clueless.

Any tips, insights, or thoughts will be much appreciated!

Hi,

If you have the ability to model your 3D parts, and assuming you build the extra parts (turrets etc) in a way that they can be nicely mounted on top of your unit (tank), then the hard part of your job is done.

Actually mounting parts to a base unit should be very easy using parenting. So the example Cannon would become a child of the Tank.

To expand this explanation a little, imagine you have a cannon GameObject, which has a 3D representation and an attached class script to it, that holds some variables (for example, at which point on the base unit it may be mounted) and some functions (for example: Shoot).

All that would be left to do is implement your “Mount()” method, which would set itself at certain coordinates and create the parenting:

// This line when in the Tank script will make the 
// current (tank) transform be a parent of the cannon
cannon.transform.parent = transform:

Well, since Unity has everything structured already in objects it’s not that hard to setup. The usual approach is to define “slots” on your base object (the player itself). Each slot will be a child gameobject which is positioned and rotated the right way. Usually you would use the Unity standards so z-axis is forward and y is up. Each of those slot objects will get a slot script. This should handle what object is currently plugged and which type can be plugged. For the slot type you would use an enum with your different types.

Each slot also should get a collider of some sort which is marked “as trigger”. A sphere collider is usually the best. This will be your “selection area” when you want assign something to it.

Your drag & drop mechanism can be implemented in various ways. Usually you just remember the object you’ve clicked (or when it’s a gui element, remember the type somehow). While dragging (with mouse button down) you raycast for slots. If you hit one you retrieve the slot-script from it and check it the type you’re draggind is compatible. If so you could visualize this somehow. Same when it’s not compatible. When the mouse button is released on a slot, you would either use the dragged object or create the selected object / prefab and attach it as child to the slot object. Set localPosition and localEulerAngles to 0,0,0 and maybe set the current plugged object reference on a public variable on the slot script for later use when the plugged object should be removed / destroyed.

It’s actually quite simple, but in the end there’re a lot small things involved. Try to keed it modular. It would also be possible to use inheritance to specify the object type. So each pluggable object is derived from a “pluggable type” and all the types are derived from a common PluggableObject (which is derived from MonoBehaviour).

Since you didn’t specify a language, i will use C# :wink:

// C#
enum ESlotType
{
    SmallWeapon,
    MediumWeapon,
    LargeWeapon,
    Engine,
}

public class Slot : MonoBehaviour
{
    public ESlotType AllowedType;
    public PlugableObject CurrentObject = null;
    public void Plug(PlugableObject aNewObject)
    {
        if (CurrentObject != null)
        }
            Destroy(CurrentObject.gameObject);
        }
        aNewObject.transform.parent = transform.parent;
        aNewObject.transform.localPosition = Vector3.zero;
        aNewObject.transform.localEulerAngles = vector3.zero;
        CurrentObject = aNewObject;
    }
}

public class PluggableObject : MonoBehaviour
{
    public ESlotType Type;
    public Texture2D GUIImage;
}

public class Laser : PluggableObject
{
    public float Damage;
    public float ROF;
    // ...
}

public class Engine : PluggableObject
{
    public float Power;
    // ...
}

With those classes you can create some prefabs which include the actual object / Mesh. Just attach for example the Laser script to the prefab and select the slot type. All your prefabs will probably be added to a list / array of your manager / GUI script where you can choose what you want to attach / buy / select.

This is a simple script which would be attached to your camera:

public class WeaponEquip : MonoBehaviour
{
    public PluggableObject[] pluggables; // assign the prefabs in the inspector
    private PluggableObject m_Dragging = null; 

    void OnGUI()
    {
        Event e = Event.current;
        if (m_Dragging == null)
        {
            foreach(PluggableObject Obj in pluggables)
            {
                GUILayout.Label(Obj.GUIImage);
                if (e.type == EventType.MouseDown && e.button == 0 && GUILayoutUtility.GetLastRect.Contains(e.mousePosition))
                {
                    m_Dragging = Obj;
                }
            }
        }
        else
        {
            var P = e.mousePosition;
            GUI.Label(new Rect(P.x,P.y,100,100),m_Dragging.GUIImage);
            if (e.type == EventType.MouseDrag || e.type == EventType.MouseUp)
            {
                Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
                RaycastHit hit;
                if (Physics.Raycast(ray, out hit, 100))
                {
                    Slot slot = hit.collider.GetComponent<Slot>();
                    if (slot != null)
                    {
                        if (slot.AllowedType == m_Dragging.Type)
                        {
                            // Show that attaching is possible
                            if (e.type == EventType.MouseUp)
                            {
                                PluggableObject newO = (PluggableObject)Instantiate(m_Dragging);
                                slot.Plug(newO);
                            }
                        }
                        else
                        {
                            // Show that attaching is NOT possible
                        }
                    }
                }
            }
            if (e.type == EventType.MouseUp)
                m_Dragging = null;
        }
    }
}

Well it got a bit longer than i expected :wink: Hopefully it helps to find your own way that fits your case :wink: