BlaXuan 发表于 2022-4-25 22:28

Unity中实现拖拽UI实现三维物体布置的效果

主要内容


[*]前言介绍
[*]Unity中简单界面搭建
[*]代码实现
[*]其他效果设置
前言介绍




主要功能:在Unity中通过点击UI按钮的图片,生成相应的三维物体。该生成物体,跟随鼠标移动。如果在指定位置,抬起鼠标左键,则在该位置生成该物体,否则销毁该物体。
思路:监听鼠标点击的UI元素,如果鼠标左键点击到三维物体对应的UI按钮元素(通过标签识别),则实例化相应的三维物体,同时让三维物体位置实时更新成鼠标转化成的三维位置,即可实现跟随鼠标操作。当鼠标左键抬起时,打开生成的三维物体的碰撞器,并在短暂延时后,销毁该物体。
在可以生成的三维物体的位置,如果实例化的三维物体销毁在生成位置,因为打开了碰撞器,则会触发生成位置的碰撞检测函数,生成进入碰撞区域的三维物体。
Unity中简单界面搭建

将所要点击的按钮标签修改为PlantName。



图示点击的生成按钮标签设置

为生成按钮对应的预制体,添加BoxCollider组件、Rigidbody组件,并如下图设置参数。



图示预制体的组件添加与参数设置

代码实现

ClickUIObject脚本(该脚本在之前文章中也出现过。主要是用于返回点击的UI元素物体。)

将ClickUIObject脚本、PlantSet脚本挂在到同一个物体上。
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;
public class ClickUIObject : MonoBehaviour
{
    public GameObject ClickObject()//判断鼠标点击的是哪个UI
    {
      PointerEventData eventDataCurrentPosition = new PointerEventData(EventSystem.current);
      eventDataCurrentPosition.position = new Vector2(Input.mousePosition.x, Input.mousePosition.y);
      List<RaycastResult> results = new List<RaycastResult>();
      EventSystem.current.RaycastAll(eventDataCurrentPosition, results);
      if (results.Count > 0)
      {
            return results.gameObject;
      }
      else
      {
            return null;
      }
    }
}
PlantSet脚本(根据点击的物体UI名称,生成物体,并跟随鼠标移动,松开鼠标,则被销毁)

using UnityEngine;
public class PlantSet : MonoBehaviour
{
    private GameObject instance;//实例化的物体
    public float distance=6f;//物体距离摄像机距离
    void Update()
    {
      if (Input.GetMouseButtonDown(0))//拖动、放置功能
      {
            CreateWithCusorObj("PlantName", "04_PlantPlanting/");//创建跟随鼠标移动的物体
      }
      WithObjOperation();//若生成了物体,其坐标跟随光标移动,松开鼠标左键,开启碰撞器后,销毁该物体
    }
    /// <summary>
    /// 创建跟随鼠标移动的物体
    /// </summary>
    private void CreateWithCusorObj(string tagName,string path)
    {
      GameObject clickGameObject = transform.GetComponent<ClickUIObject>().ClickObject();
      if (clickGameObject!=null)
      {
            print(clickGameObject.name);
            if (clickGameObject.tag == tagName)//实例化物体并设置父物体,UIButton
            {
                //string resourceName = clickGameObject.transform.Find("Text").GetComponent<Text>().text;//找到点击植物的名称
                string resourceName = clickGameObject.name;//找到点击植物的名称
                instance = (GameObject)Instantiate(Resources.Load(path + resourceName));
            }
      }
    }
    /// <summary>
    /// 若生成了物体,其坐标跟随光标移动,松开鼠标左键,开启碰撞器后,销毁该物体
    /// </summary>
    private void WithObjOperation()
    {
      if (instance != null)
      {
            Vector3 mousePosition = new Vector3(Input.mousePosition.x, Input.mousePosition.y, distance); //获取屏幕坐标
            Vector3 mouseWorldPos = Camera.main.ScreenToWorldPoint(mousePosition); //屏幕坐标转世界坐标
            instance.transform.position = mouseWorldPos;//跟随鼠标
            if (Input.GetMouseButtonUp(0))//只有鼠标抬起时,启用碰撞器,防止碰撞到路径上其他选项
            {
                instance.transform.GetComponent<BoxCollider>().enabled = true;//开启实例化物体子物体Text的碰撞器
                Destroy(instance, 0.05F);//延时销毁,发挥碰撞器作用
            }
      }
    }
}
OnColliderPlantTest脚本(根据进入碰撞器物体名称,生成子物体)

将该脚本挂载到生成位置的物体上,即可。
using UnityEngine;
public class OnColliderPlantTest : MonoBehaviour
{
    private string resourceName = "";//示例化的物体名称
    private string tempName = "";//临时物体名称,用于判断是否改变了所要生成的物体
    private bool isFirst = true;//是否第一生成物体,第一次直接生成,否则,判断时候需要重新生成不同的物体.
    private void OnCollisionStay(Collision collision)
    {
      if (!isFirst) tempName = resourceName;//不是第一次生成,保存已经生成的植物名称
      resourceName = collision.gameObject.name;//更新点击植物的名称
      string instanceName = "";
      foreach (var c in resourceName)
      {
            if (c != '(')//生成调用物体的名称,要求不带(clone)
            {
                instanceName += c;
            }
            else
            {
                break;
            }
      }
      if (isFirst)//第一次生成,实例化物体,修改isFirst
      {
            CreatePlant(instanceName);
            isFirst = false;
      }
      else
      {
            if (tempName != resourceName)
            {
                Destroy(transform.GetChild(0).gameObject);//销毁已经生成的物体
                CreatePlant(instanceName);
            }
      }
    }
    private void CreatePlant(string name)//在摆放位置实例化物体
    {
      print("实例化:" + name);
      GameObject instance = (GameObject)Instantiate(Resources.Load("04_PlantPlanting/" + name), transform.position, transform.rotation);
      instance.transform.SetParent(transform);
    }
}
其他效果设置

1、当布置的物体不合适,会有错误提示效果。



图示运行效果

思路:生成三维物体的原理跟上面代码,只是多加了正确与错误的判定。如果摆放正确,将点光源的光调成绿色,错误改为红色。在这里布置的类型有两种,一个是乔木,一个是灌木。下面脚本中,isTree为True时,为应该布置乔木,否则,布置灌木,灌木有两个物体,所以也需要进行判定一下,符合其中的一种,即为布置正确。
代码实现

using UnityEngine;

public class OnColliderPlantPratice : MonoBehaviour
{
    public bool isTree;//该位置是否应该放置乔木(Tree)
    public Transform mLight;//位置对应的点光源
    private string resourceName = "";//示例化的物体名称
    private string tempName = "";//临时物体名称,用于判断是否改变了所要生成的物体
    private bool isFirst = true;//是否第一生成物体,第一次直接生成,否则,判断时候需要重新生成不同的物体
    private void OnCollisionStay(Collision collision)
    {
      if (!isFirst) tempName = resourceName;//不是第一次生成,保存已经生成的植物名称
      resourceName = collision.gameObject.name;//更新点击植物的名称
      string instanceName = "";
      foreach (var c in resourceName)
      {
            if (c != '(')//生成调用物体的名称,要求不带(clone)
            {
                instanceName += c;
            }
            else
            {
                break;
            }
      }
      if (isFirst)//第一次生成,实例化物体,修改isFirst
      {
            CreatePlant(instanceName);
            isFirst = false;
      }
      else
      {
            if (tempName != resourceName)
            {
                Destroy(transform.GetChild(0).gameObject);//销毁已经生成的物体
                CreatePlant(instanceName);
            }
      }
      if (!isTree)//如果应该放置灌木
      {
            if(resourceName== "ShrubButton1(Clone)" || resourceName == "ShrubButton2(Clone)")
            {
                print("放置正确");
                mLight.GetComponent<Light>().color = Color.green;
            }
            else
            {
                mLight.GetComponent<Light>().color = Color.red;
                print("放置错误");
            }
      }
      else
      {
            if (resourceName == "TreeButton(Clone)")
            {
                mLight.GetComponent<Light>().color = Color.green;
                print("放置正确");
            }
            else
            {
                mLight.GetComponent<Light>().color = Color.red;
                print("放置错误");
            }
      }
    }
    private void CreatePlant(string name)//在摆放位置实例化物体
    {
      print("实例化:"+name);
      GameObject instance = (GameObject)Instantiate(Resources.Load("04_PlantPlanting/" + name), transform.position, transform.rotation);
      instance.transform.SetParent(transform);
    }
}
将该脚本挂载到生成位置的物体上,设置该位置生成的物体类型,并指定需要的改变颜色的点光源物体,具体如下图所示。



图示脚本参数的设置

2、只有布置特定物体,才能在指定位置生成物体。布置正确显示绿色,错误显示红色。



图示运行效果

OnColliderArchitectPratice脚本
using System.Collections;
using UnityEngine;
public enum ArchirectType { Pavilion, Sculpture, Non };//该位置是否应该放置亭子(Pavilion)、雕塑(Sculpture)、Non
public class OnColliderArchitectPratice : MonoBehaviour
{

    public Transform mLight;//位置对应的点光源
    private string resourceName;
    public ArchirectType archirectType;
    private void OnCollisionStay(Collision collision)
    {
      resourceName = "";
      string instanceName = "";
      resourceName = collision.gameObject.name;//找到点击植物的名称
      print(resourceName);
      foreach (var c in resourceName)
      {
            if (c != '(')//生成调用物体的名称,要求不带(clone)
            {
                instanceName += c;
            }
            else
            {
                break;
            }
      }
      if (transform.childCount == 0)
      {
            if (archirectType == ArchirectType.Non)
            {
                mLight.GetComponent<Light>().color = Color.red;
                print("放置错误");
                StartCoroutine(ResetLight());
            }
            else if (archirectType == ArchirectType.Pavilion)
            {
                if (resourceName == "PavilionButton(Clone)")
                {
                  mLight.GetComponent<Light>().color = Color.green;
                  CreatePlant(instanceName);
                  print("放置正确");
                }
                else
                {
                  mLight.GetComponent<Light>().color = Color.red;
                  print("放置错误");
                  StartCoroutine(ResetLight());
                }
            }
            else if (archirectType == ArchirectType.Sculpture)
            {
                if (resourceName == "SculptureButton1(Clone)" || resourceName == "SculptureButton2(Clone)")
                {
                  print("放置正确");
                  mLight.GetComponent<Light>().color = Color.green;
                  CreatePlant(instanceName);
                }
                else
                {
                  mLight.GetComponent<Light>().color = Color.red;
                  print("放置错误");
                  StartCoroutine(ResetLight());
                }
            }
      }
    }
    private void CreatePlant(string name)//在摆放位置实例化物体
    {
      GameObject instance = (GameObject)Instantiate(Resources.Load("05_ArchitectSet/" + name));
      instance.transform.position = transform.position;
      instance.transform.SetParent(transform);
    }
    IEnumerator ResetLight()
    {
      yield return new WaitForSeconds(1.5f);
      mLight.GetComponent<Light>().color = new Color(1, 0.3175f, 0, 1);
    }
}
将该脚本挂载到生成位置的物体上,设置该位置生成的物体类型,并指定需要的改变颜色的点光源物体,具体如下图所示。



图示脚本参数的设置
页: [1]
查看完整版本: Unity中实现拖拽UI实现三维物体布置的效果