找回密码
 立即注册
查看: 407|回复: 0

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

[复制链接]
发表于 2022-4-25 22:28 | 显示全部楼层 |阅读模式
主要内容


  • 前言介绍
  • 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[0].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);
    }
}
将该脚本挂载到生成位置的物体上,设置该位置生成的物体类型,并指定需要的改变颜色的点光源物体,具体如下图所示。



图示  脚本参数的设置

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

×
懒得打字嘛,点击右侧快捷回复 【右侧内容,后台自定义】
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Unity开发者联盟 ( 粤ICP备20003399号 )

GMT+8, 2024-11-21 21:14 , Processed in 0.193445 second(s), 27 queries .

Powered by Discuz! X3.5 Licensed

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表