[Unity]建造放置系统BuildSystem




Build System


下载完毕,导入。





下面这个脚本,直接挂载到一个空物体,新建一个 3D物体 立方体,将其设置为 BuildGameObject变量的值(如下图所示)。新建一个 按钮button,来触发placeBool函数即可 实现放置基本的物体。


using UnityEngine;
using BuildSystem;


public class BuildManage : MonoBehaviour {


    public Material ghostMaterial;//放置物体 时候的 材质
    private Material goMaterial;


    //**********************************************************************************************
    [Header("Input Settings")]


    [Tooltip("Key to press to enable the builder mode. This is also used by ObjectSelector")]
    public KeyCode toggleKey = KeyCode.E;


    [Tooltip("Key to press to place a item in the scene")]
    public KeyCode placeKey = KeyCode.Mouse0;


    [Tooltip("Key to press rotate (forward) the object based on snapRotaionDeg")]
    public KeyCode positiveRotateKey = KeyCode.Mouse1;


    [Tooltip("Key to press rotate (backward) the object based on snapRotaionDeg")]
    public KeyCode negativeRotateKey = KeyCode.None;


    [Header("More Settings")]
    [Tooltip("Disable the remover script when placer is active. Note: remover must be next to this script")]
    public bool shouldToggleRemover = true;


    [Tooltip("Max distance from camera where you can place objects")]
    public float maxPlaceDistance = 100f;
    /****************************************************
    * Editor Interface
    * *************************************************/


    [Tooltip("Camera used to raycast and find place position, if empty the script will try to use the main camera")]
    public Camera cam;




    public LayerMask movementMask;//设置 LayerMask 
    public GameObject buildGameObject;//建设的物体


    /****************************************************
    * Variables
    * *************************************************/


    bool isActive = true;


    bool canPlace = false;


    bool mouseIsNotOnUI = true;


    //object rotation
    float objectSnapCurrentRotaion = 0;


    //object pivot
    bool usingFakePivot = false;


    /****************************************************
    * Events
    * *************************************************/


    public delegate void BuildEvent();


    public event BuildEvent OnGhostObjectCreation;
    public event BuildEvent OnGhostObjectDestroy;
    public event BuildEvent OnGhostObjectPlace;
    /****************************************************
        * Components & references
        * *************************************************/


    Transform ghostObjInstance;


    Transform myTransform;
    
    // Use this for initialization
    void Start () {


        if (cam == null)
            cam = CameraManager.instance.nowCamera;
        myTransform = transform;
    }


    void Update()
    {
        //点击鼠标左键
        if (Input.GetMouseButtonDown(0) && canPlace == true)
        {
            PlaceGhostObject();
        }


    }
    private void FixedUpdate()
    {
        //update ghost object position
        if (canPlace && ghostObjInstance != null)
        {
            MoveGhostObject();
        }
    }


    //由按钮触发
    public void placeBool()
    {
        canPlace = true;
        CreateGhostObject();
    }
        /****************************************************
         * Ghost Object Creation & Movement & Place
         * *************************************************/


        /// <summary>
        /// Create ghost object
        /// </summary>
        void CreateGhostObject()
        {
            DestroyGhostObject();


           /*if (objectToPlace == null)
            {
                Debug.LogError("No item to instantiate! Aborting ghost creation.");
                return;
            }*/
      
            ghostObjInstance = Instantiate(buildGameObject, myTransform.position, Quaternion.identity).GetComponent<Transform>();


            goMaterial =ghostObjInstance.transform.GetComponent<MeshRenderer>().material;
            ghostObjInstance.transform.GetComponent<MeshRenderer>().material = ghostMaterial;
            ghostObjInstance.transform.gameObject.layer = 0;//设置 新建物体 时候,的预览 幻影 物体 的层级。使其 不BUG
        //reset old object rotation
        //if (resetRotationAfterPlace)
        //    objectSnapCurrentRotaion = 0;


        //check where is the pivot, if it is not in the base create a fake one
        usingFakePivot = false;
            


            Vector3 pivotOffsetExtra;
            bool objPivotIsBase = isPivotInBase(ghostObjInstance, out pivotOffsetExtra);


            //create a fake pivot if the real one is not in base
            if (!objPivotIsBase)
            {
                ghostObjInstance = CreateBasePivot(ghostObjInstance, pivotOffsetExtra);
                usingFakePivot = true;
            }


            if (OnGhostObjectCreation != null)
            {
                OnGhostObjectCreation();
            }
        }




        /// <summary>
        /// Move the ghost object
        /// </summary>
        void MoveGhostObject()
        {
            Ray ray = cam.ScreenPointToRay(Input.mousePosition);
            RaycastHit hit;


            //射线只碰撞到 有碰撞体的物体 ,距离为 100 ,并且 LayerMask层级为 Floor的物体
            if (Physics.Raycast(ray, out hit, maxPlaceDistance, movementMask))
            {
                //set object position to hit point
                Vector3 pos = hit.point;
                ghostObjInstance.transform.position = pos;


             AlignGhostToSurface(ghostObjInstance, hit.normal);
          }
        }
    
        /// <summary>
        /// Place the object in the scene
        /// </summary>
        void PlaceGhostObject()
        {
            if (ghostObjInstance != null)
            {


                Debug.Log("Created: " + ghostObjInstance.name);


                 if (usingFakePivot) // remove fake pivot if using one
                 {
                     ghostObjInstance = ghostObjInstance.GetComponent<PivotHelper>().DeletePivot();
                 }
             
            //ghostObjInstance.transform.GetComponent<Renderer>().material = goMaterial;
            //place real object in scene
             GameObject go = Instantiate(buildGameObject, ghostObjInstance.position, ghostObjInstance.rotation);
                go.layer = 8;//设置 物体的层级


                //Debug.Log("ghostObjInstance.transform.childCount:"+ ghostObjInstance.transform.childCount);
                Destroy(ghostObjInstance.gameObject);


                if (OnGhostObjectPlace != null)
                {
                    OnGhostObjectPlace();
                }


                //CreateGhostObject(); //create a new ghost object
            }
            else Debug.LogError("Unable to spawn object, ghost reference is null!");


            canPlace = false;
        }
    
        /// <summary>
        /// Remove ghost object from scene
        /// </summary>
        void DestroyGhostObject()
        {
            if (ghostObjInstance != null)
            {
                Destroy(ghostObjInstance.gameObject);


                /*if (OnGhostObjectDestroy != null)
                {
                    OnGhostObjectDestroy();
                }*/
            }
        }
    
    /****************************************************
    * Pivot helpers 中心点
    * *************************************************/


    /// <summary>
    /// Create a pivot parent to better place the object
    /// </summary>
    /// <param name="item">Item to use</param>
    /// <param name="renderer">Renderer of the item</param>
    /// <param name="pivotOffset">Offset of the pivot</param>
    /// <returns></returns>
    Transform CreateBasePivot(Transform item, Vector3 pivotOffset)
    {
        var normMesh = item.GetComponentInChildren<MeshRenderer>();
        var skinMesh = item.GetComponentInChildren<SkinnedMeshRenderer>();


        if (normMesh == null && skinMesh == null)
        {
            Debug.LogError("No renderers found!");
            return null;
        }


        GameObject pivotG = new GameObject("Temp_Ghost_Pivot_Parent"); // create parent
        Transform pivotT = pivotG.transform;


        pivotG.AddComponent<PivotHelper>(); // add helper class to remove the pivot when object is spawned


        //get mesh center
        Vector3 meshCenter = (normMesh != null) ? normMesh.bounds.extents : skinMesh.bounds.extents;
        // apply pivot delta
        meshCenter.x = pivotOffset.x;
        meshCenter.z = pivotOffset.z;
        meshCenter.y += pivotOffset.y;




        item.SetParent(pivotT); // set the current object as parent
        item.localPosition = meshCenter; // move the object and leave the parent object in the pivot position


        return pivotT;
    }
    /// <summary>
    /// Check if the object pivot is in center or not.
    /// This function returns the pivot Offset (can be Vector3.zero)
    /// </summary>
    /// <param name="item">Item to use</param>
    /// <param name="renderer">Renderer attached to the item</param>
    /// <param name="pivotOffset">Offset of the pivot to be in base</param>
    /// <returns></returns>
    bool isPivotInBase(Transform item, out Vector3 pivotOffset)
    {
        var normMesh = item.GetComponentInChildren<MeshRenderer>();
        var skinMesh = item.GetComponentInChildren<SkinnedMeshRenderer>();


        if (normMesh == null && skinMesh == null)
        {
            Debug.LogError("No mesh renderer found!");
            pivotOffset = Vector3.zero;
            return false;
        }


        var pivotMargin = (normMesh != null) ? normMesh.bounds.extents.y * 2 / 3 : skinMesh.bounds.extents.y * 2 / 3; //set the base pivot margin. 
                                                                                                                      //Its' height must be lower than obj center * 2/3


        Vector3 delta = item.position - ((normMesh != null) ? normMesh.bounds.center : skinMesh.bounds.center);
        if (delta.magnitude >= pivotMargin && delta.y < 0) //delta.y < 0 fix issues that not centerd pivots above the object center were taken as base pivots
        {
            pivotOffset = Vector3.zero;
            return true;
        }
        else
        {
            pivotOffset = delta; // save pivot delta to use to create a fake pivot
            return false;
        }
    }


    /****************************************************
        * Ghost Object Alignament 
        * *************************************************/


    /// <summary>
    /// Align ghost object to surface based on raycast hit normal
    /// </summary>使得 地板 layer 层级 的物体 ,创建的 时候 可以 避免相互之间 重叠
    /// <param name="itemToAlign">Item to align</param>
    /// <param name="hitNormal">Normal to use to align object</param>
    void AlignGhostToSurface(Transform itemToAlign, Vector3 hitNormal)
    {
        if (itemToAlign == null) return;


        itemToAlign.rotation = Quaternion.FromToRotation(Vector3.up, hitNormal) * Quaternion.Euler(new Vector3(0, objectSnapCurrentRotaion, 0));


    }




}

问题:

1.出现 放置物体 重叠 BUG 解决办法




参考资料:

1.

Unity3D独立游戏开发日记(二):摆放建筑物

2.

Unity 使用自定义资源(.asset)配置数据

3.


  • 8
    点赞
  • 41
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值