how to clone several game objects in a way that clone properties of one can be adjusted to match all others in scene view

試著忘記壹切 提交于 2019-11-28 13:18:41

I don’t want to use prefabs

The new prefab system in Unity is exactly what you need. It fits all of your requirements:

  • Clone several unique game objects

    The prefab system is made for cloning unique gameobjects. It even supports prefab nesting.

  • I don't want to achieve this at runtime

    Great, prefabs only update globally when you click the override button in the editor.

  • I need a way to turn off the this repeated property/component replication on each clone

    That's equivalent to unpacking the object (breaking the connection).

If you have a good reason to avoid using prefabs, you can always write a custom script that tracks changes in the properties you want to share, and updates all other objects immediately. You can make that script run in edit mode by adding the [ExecuteInEditMode] attribute to the class it's in, just don't forget to disable it when running the project. Again, I highly recommend using prefabs instead.

You should use a ScriptableObject as data container and attach that to the gameobject, all clones will use the same synchronized ScriptableObject.

You should use events. Unity3d tutorials has a good, simple explanation: https://unity3d.com/learn/tutorials/topics/scripting/events

Ada Nub

Is this only for editing the objects in the editor? If so, then it sounds like prefabs are the way to go; you can directly edit the prefab and all of its 'clones' in the scene will have all changes, including all monobehaviours, transforms, and whatnot replicated to that of the prefab.

If you need this to work at runtime, then you will likely need some code to do this for you. You haven't quite provided enough clarification as to what exactly you want to do, so for the below example I'll assume that you have a gameobject with a mesh or sprite component, and want its size/scale modified alongside all of its "clones";

using UnityEngine;
using System.Collections.Generic;

public class ShapeClone : MonoBehaviour
{
    //This will hold references to the other "clone" gameobjects.
    public List<GameObject> otherClones = new List<GameObject>();

    //All the "clones" in the list otherClones will have their scale matched to this gameobject's scale
    public bool leader;


    private void Update()
    {
        if (leader) //Only change other clones' scales if marked as leader, to avoid every single clone
                    //overriding each other's scale every single frame, which could be rather chaotic
        {
            for (int i = 0; i < otherClones.Count; i++)
            {
                //setting each of the other clones' scale to that of this object.
                otherClones[i].transform.localScale = this.transform.localScale;
            }
        }
    }
}

The above is a brief example to give you an idea and is by no means extensive, but you should be able to apply it to what you're trying to do; for example, if you wanted to replicate the colour of sprites across gameobjects instead, you can modify otherClones to be a list of Sprite references instead, and instead of setting the scale in update, you can set the colour of each of the Sprite components to that of this object.

If you're only needing this functionality in the editor and not during runtime, though - I highly recommend going with the first option using prefabs, as it will give you far more functionality at a fraction of the cost, performance wise.

pseudoabdul

It sounds like you have an object that has several clones. You want changing the shape or dimensions of any of those objects to affect the other ones?

For this to happen, each object needs to know about the other ones. You can do this decentralized (each object contains a reference to each other) or centralized (one object governs the rest).

The centralized approach is more simple so I'll give a simple example.

public class Shape
{
    public int length;
}

public class ShapeCentral
{
    public List<Shape> shapes = new List<Shape>();

    public void CloneShape()
    {
        //instantiate new shape
        shapes.Add(new Shape());
    }

    public void SetCloneLength(int l)
    {
        shapes.ForEach(x => x.length = l);
    }
}

As you can see, one object can control all the clones at once. The trick is to not create clones using other methods or you will run into trouble.

If you want to tighten up your variable access (which I recommend, its a good exercise) you could use a publisher/subscriber pattern. In this, when a new clone is instantiated, it subscribes to the SetCloneLength method. When you want to change the length, the central class publishes that message and it is sent to all the subscribers.

The difference here is that in my example, the central class needs to keep track of all the clones, in publisher/subscriber, you don't.

Create script CopycatManager that will hold a leader and then use dedicated setters for copying the other object properties that have the same type. If a property is a default one may need to set up either a proxy of such property within' the script or play with triggers. I would recommend proxy. Like this:

class CopycatManager {

    public GameObject leader;

    SomeAttributeType attributeToCopyFromLeader {get; private set}

    void Start () {
        // The first CopycatManager to start is the leader
        List<CopycatManager> allCMs = parent.GetComponentsInChildren();
        CopycatManager foundLeader = allCMs.Find(o => o.leader == o);
        if (foundLeader == null) {
            // There's no leader yet, set yourself a leader
            leader = this;
        } else {
            // Found a leader, accept
            leader = foundLeader;
        }
    }

    public void SetAttribute (SomeAttributeType newVal) {
        // If we're setting the attribute of the leader - we should set this attribute for all children
        if (leader == gameObject) {
            // Find all copycat manager scripts attached to children of current parent
            // Meaning siblings
            // WARNING: It will include children of siblings and the leader itself
            // WARNING: It will not include parents of the Copycat Manager type, add if required
            List<CopycatManager> allCMs = parent.GetComponentsInChildren();
            foreach (CopycatManager manager in allCMs) {
                SetAttributeFromLeader (newVal);
            }
        } else {
            // Non-leader is attempting to change attribute - call leader
            leader.SetAttribute(newVal);
        }
    }

    // Called by leader to each child
    public void SetAttributeFromLeader (SomeAttributeType newVal) {
        attributeToCopyFromLeader = newVal;
    }
}

Make sure to assign a new leader if the old one destroyed. Only destroy objects with CopycatManager through dedicated function.

make all items that need scaling children of an empty called WorldObjects then scale the world object, it will scale all its children accordingly. you can then either manually or through script remove the parent to make objects independent. best way without prefabs...

use a singleton class. add that script to all the objects, then you can make a call to one and it will adjust all of them.

you can also do this with a static class, but the singleton approach is cleaner, and gives you more options.

public class MySingleton
{

    private static MySingleton fetch; // keep the static reference private

    public bool myBool = false;

    // and expose static members through properties
    // this way, you have a lot more control over what is actually being sent out.
    public static bool MyBool { get { return fetch ? fetch.myBool : false; } }

    void Awake()
    {
        fetch = this;
    }
}

read here for some great information on both options!

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!