问小白 wenxiaobai
资讯
历史
科技
环境与自然
成长
游戏
财经
文学与艺术
美食
健康
家居
文化
情感
汽车
三农
军事
旅行
运动
教育
生活
星座命理

Unity零基础跟练制作独立游戏:拖拽事件、预制体、跨类调用方法

创作时间:
作者:
@小白创作中心

Unity零基础跟练制作独立游戏:拖拽事件、预制体、跨类调用方法

引用
CSDN
1.
https://blog.csdn.net/weixin_45633843/article/details/144721328

本篇文章是Unity游戏开发入门教程系列的第五篇,主要讲解了如何实现按钮拖拽功能、创建和使用预制体以及跨类调用方法和传递参数。通过本篇教程,读者可以掌握这些基本的Unity开发技能,为制作独立游戏打下坚实的基础。

一、按钮拖拽功能实现

由于暂时不需要双击和长按的鼠标输入事件,先在节点中删去Mouse Event 脚本。点标签右上角打开更多选项,选择Remove Component。

首先来实现节点拖拽功能,在nodes脚本中添加函数:

public void OnMouseDrag()
{
    Debug.Log("正在拖拽");
}

把函数与button上的event Trigger 组件绑定:先把之前Event Trigger中留下的无效的长按事件删掉,添加拖拽事件,绑定nodes物体的nodes脚本,选其中的onMouseDrag函数。

测试观察控制台日志,发现鼠标按住按钮并拖动时,控制台会在每一帧持续输出“正在拖拽”,并且只有鼠标同时按住并拖动才会输出日志。

要实现拖动按钮的操作,需要鼠标在按钮上按住并拖动,并持续更新按钮的位置到鼠标实时所在的位置

为了获取鼠标所在的位置,就需要先了解Unity当中坐标系的概念。鼠标和按钮所在的坐标系并不相同,按钮所在的是世界坐标系,而鼠标位置使用的是屏幕坐标系,二者并不统一。

简单用代码演示下:

void Update()   //Update:在游戏中每帧都会调用一次的方法
{
    //演示鼠标的屏幕坐标系和节点的世界坐标系
    if (Input.GetMouseButtonDown(0))
    {
        Debug.Log("MousePosition: " + Input.mousePosition);
        Debug.Log("NodePosition:" + nodeButton.GetComponent<Transform>().position);
    }
}

下面控制台中,是我依次点击屏幕的左下角和中间节点所在处产生的输出 ,分别是
MousePosition:(5,8,0)NodePosition:(0,0,90)

MousePosition:(575,265,0)NodePositon:(0,0,90)

可以看到,屏幕坐标系和世界坐标系明显不同,如果直接将鼠标的位置赋值给节点的位置,一定不会得到理想的结果。所以需要先将鼠标位置信息转换为世界坐标系下的位置信息

在刚刚创建的OnMouseDrag函数中添加下面代码,令Nodes自身的位置与转换后的鼠标位置信息同步。

//获取鼠标所在的位置,将鼠标位置信息转换为世界坐标系对应的位置信息
Vector2 pos = Camera.main.ScreenToWorldPoint(Input.mousePosition);
this.transform.position = pos;  //更改物体自身的位置到鼠标对应位置

注:在C#中,this关键字表示当前对象的实例,this可以访问当前对象的属性、方法等。
这里的this代表的是unity中的Nodes这个物体。因为Nodes这个物体是最终实例化的对象。
这里脚本中不存在重名的属性,this可以省略。

保存后测试,可以看到节点成功被拖动。

二、创建和使用预制体

游戏中会存在多个功能属性相似的节点,为了方便以后统一调用,可以将这个节点保存为预制体

在Assets文件夹内创建新的文件夹,命名为Prefabs,用来保存预制体。打开Prefabs,把层级目录中的Nodes拖入Prefabs中,这样就创建了一个节点的预制体。节点变成预制体后,也就可以作为模板,使用代码随意调用,省去了很多重复工作。

创建预制体后,删掉场景中的Nodes,在Script中创建一个新的脚本,命名为Game。对脚本进行代码的编写:

public class Game : MonoBehaviour
{
    //声明一个变量,用来指明要创建的是哪个预制体
    public Nodes node;
    // Start is called before the first frame update
    void Start()
    {
        this.CreateNode();
    }
    //创建一个方法,每次调用该方法都可以实现创造物体
    public void CreateNode()
    {
        Debug.Log("开始创建物体");
    }
    // Update is called once per frame
    void Update()
    {
        
    }
}

首先在类当中声明一个变量,用来指明要创建的是哪个预制体,这里Nodes是脚本Nodes中的类。

之后创建一个方法CreateNode,之后每次调用方法都会根据预制体创建物体。现在其中填写上debug语句方便理解。

回到编辑器,将Game这个脚本附加到Canvas,使脚本能够生效。

之后把预制体nodes绑定在Node属性上。以后我们对node这个变量的改动都会反映到预制体创建的物体上。

注意要将Nodes这个预制体拖选到菜单,然后编辑器会自动从物体上获取对应的脚本。

这里括号外代表的是预制体,括号内代表的是Nodes脚本。

经过测试,脚本中的方法成功调用。

给CreateNode方法中添加下面的代码:

Instantiate(node, this.transform);  //创建一个物体,并让他从属于canvas的子物体

这里创建了一个将canvas指定为父物体的节点复制体。其位置和旋转取默认值。

Instantiate是对预制体进行实例化的方法,默认格式为Instantiate(prefab, position, rotation, parentTransform)。

其中prefab代表作为模板的预制体,position表示创建的新对象的位置坐标,rotation表示新对象相对世界坐标的旋转角度。在最后可以添加上希望成为新实例父物体的unity对象的transform组件来指定父物体。

prefab是必要的,position、rotation、parentTransform都可省略。位置和旋转省略则取预制体当中的默认值,父物体省略则不会指定父物体。

回到编辑器测试,界面中心位置创建了初始节点,且其他功能与预制体相同。

物体列表中会多出用脚本创建的子物体nodes(Clone),停止运行后消除。

三、跨类调用方法和传递参数

在nodes脚本中添加新的函数initData:

public void initData(string titleText)  //初始化节点数据
{
    //在创建节点的同时,修改它的文本内容
    nodeTitle.text = titleText; //将标题文本设置为调用该方法时传递过来的字符串
}

这个函数用来初始化节点的数据,在调用该函数时,会将节点标题改为调用函数输入的文本。

修改Game脚本中的CreateNode函数:

public void CreateNode()
{
    //Debug.Log("开始创建物体");
    Nodes n = Instantiate(node, this.transform);  //创建一个物体,并让他从属于canvas的子物体;这里的this指脚本附加的canvas
    //instantiate是对预制体进行实例化的方法,在最后添加上希望成为新实例父物体的unity对象的transform组件来指定父物体
    n.initData("NewTitle");
}

这里由于需要对新对象的内容进行修改,先用临时变量保存下来。之后n.initData() 成功调用n对应的Notes类下的 public 公共方法 initData ,由于在方法中要求了接收string参数,用()调用后必须加入传递的string数据

这样就成功在Nodes类新建了一个public方法,并在Game类的CreateNode方法中跨类调用了该方法,并通过参数传递成功为节点的标题赋值。

可以看到运行测试后标题改变为初始化要求的值。

本篇继续实现了拖拽这一鼠标事件,不过设计还是过于简单,鼠标若在节点偏离中心点的位置拖拽,节点中心一开始就会瞬移到鼠标位置。 学习了创建预制体和通过预制体生成对象的方法。学习了跨类调用自定义方法,并传递参数的操作。

© 2023 北京元石科技有限公司 ◎ 京公网安备 11010802042949号