Unity学习-脚本基础part01

一、脚本相关概念

1-脚本的定义

  • 脚本可以附加在游戏物体上,用于定义游戏对象行为的指令代码,如:xxx.cs文件
  • Unity支持三种高级编程语言:C#、javascript和Boo Script(Unity4以前支持)

2-语法结构

//using命名空间,类比java的import包
using 命名空间;
public class 类名: MonoBehaviour
{
    void 方法名();
    Debug.Log("调试显示信息");
    print("本质就是Debug.Log方法");
}
  • 文件名与类名必须一致
  • 写好的脚本必须附加到物体上才执行
  • 附加到游戏物体的脚本类必须从MonoBehaviour类继承

3-脚本编译过程

  • 编译运行过程:
  • 源代码:(CLS)—>中间语言(dll文件)–(Mono Runtime)- >机器码

4-创建脚本文件

鼠标右键创建即可,选择C# script

image-20210823223312308

image-20211207215437589

在unity中创建出的C#脚本文件模板内部的构造:

using UnityEngine;
using System.Collections;

public class demo01 : MonoBehaviour {
	// 默认会带两个方法,如果不使用请删除
    
	// Use this for initialization
	void Start () {
	
	}
	
	// Update is called once per frame
    // Update方法不用的话就把它删除,因为此方法会被不断的调用,大概0.02s被调用一次
	void Update () {
	
	}
}

模板也是可以自定义的,我们到unity的目录下:

image-20210823224023546

修改为:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
/// <summary>
/// 
/// </summary>
public class #SCRIPTNAME# : MonoBehaviour
{


}

5-脚本在Unity中的一些操作

  1. 拖动脚本文件到游戏对象上,可以发现创建了一个对象,通过这个脚本对象我们可以进行许多操作

image-20211207220449834

  1. 然后我们修改一下脚本内容:
public class NewBehaviourScript : MonoBehaviour
{

    //public类型默认会在Insepctor中显示
    public int A;

    //private类型默认不会在Insepctor中显示 
    private string B = "abc";

    //[SerializeField]
    //作用:在Unity编译器中显示当前private的字段
    [SerializeField]
    private bool C = true;

    //[Range(1,100)]
    //作用:在Unity编译器中限定此字段的值为0~100
    [Range(1, 100)]
    public int D = 10;

    //[HideInInspector]
    //作用:在Unity编译器中隐藏当前public的字段
    [HideInInspector]
    public float E = 1.0f;

}
  1. 查看我们之前的游戏对象的Inspector窗口中的脚本位置,发现出现相关的变量,而且可以进行设置

image-20211207232229297

6-脚本生命周期(重要)

6-1.定义

  • Unity脚本从唤醒到销毁的过程
  • 消息:当满足某种条件Unity引擎自动调用的函数
  • 脚本生命周期也被称为必然事件

6-2.初始阶段

  • Awake()唤醒:当物体载入时立即调用1次;常用于在游戏开始前进行初始化。可以判断当满足某种条件执行此脚本,然后使用此语句this.enable=true,表示会被调用。this.enable=false,之后的方法就不会被执行了。
  • OnEnable()当可用:每当脚本对象启用时调用。
  • Start()开始:物体载入且脚本对象启用时被调用1次。常用于数据或游戏逻辑初始化,执行时机晚于Awake。
/// <summary>
/// 脚本生命周期
/// </summary>
public class LIfeCycle : MonoBehaviour
{
    //执行时机:创建游戏对象时,立即执行1次 (与脚本禁用与否无关)
    //作用:初始化
    private void Awake()
    {
        Debug.Log("Awake()方法被调用了!");
    }

    //执行时机:每当脚本对象启用时调用,执行1次
    //作用:初始化
    private void OnEnable()
    {
        Debug.Log("OnEnable()方法被调用了!");
    }

    //执行时机:创建游戏对象时,且脚本未被禁用时,执行1次
    //作用:初始化
    private void Start()
    {
        Debug.Log("Start()方法被调用了!");
    }
}

将脚本挂到游戏对象上,测试一下

6-3.物理阶段

  • FixedUpdate()表示固定更新:脚本启用后,固定时间被调用,适用于对游戏对象做物理操作,例如移动等。固定是0.02秒更新一次,这个数值我们可以进行更改,设置更新频率"Edit" --> “Project Setting”–>“Time” --> “Fixed Timestep” 值,默认为0.02s。
  • OnCollisionXXX()碰撞:当满足碰撞条件时调用。
  • OnTriggerXXX()触发:当满足触发条件时调用。

6-4.输入事件

需要碰撞器Collider,即unity中的这些collider:

image-20210829205632654

  • OnMouseEnter()鼠标移入:鼠标移入到当前Collider时调用。
  • OnMouseOver()鼠标经过:鼠标经过当前Collider时调用。
  • OnMouseExit()鼠标离开:鼠标离开当前Collider时调用。
  • OnMouseDown()鼠标按下:鼠标按下当前Collider时调用。
  • OnMouseUp()鼠标抬起:鼠标在当前Collider上抬起时调用。

6-5.游戏逻辑

  • Update()更新:脚本启用后,每次渲染场景时调用,频率与设备性能及渲染量有关
  • LateUpdate()延迟更新:在Update函数被调用后执行,适用于跟随逻辑

6-6.场景渲染

  • OnBecameVisible()当可见:当Mesh Renderer在任何相机上可见时调用
  • OnBecameInvisible()当不可见:当Mesh Renderer在任何相机上都不可见时调用

6-7.结束阶段

  • OnDisable()当不可用:对象变为不可用或附属游戏对象非激活状态时此函数被调
  • OnDestroy()当销毁:当脚本销毁或附属的游戏对象被销毁时被调用
  • OnApplicationQuit()当程序结束:应用程序退出时被调用

6-8.示例

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

/// <summary>
/// 脚本生命周期
/// </summary>
public class LIfeCycle : MonoBehaviour
{
    //===================初始阶段===================
    //执行时机:创建游戏对象时,立即执行1次 (与脚本禁用与否无关)
    //作用:初始化
    private void Awake()
    {
        Debug.Log("Awake()方法被调用了!");
    }

    //执行时机:每当脚本对象启用时调用,执行1次
    //作用:初始化
    private void OnEnable()
    {
        Debug.Log("OnEnable()方法被调用了!");
    }

    //执行时机:创建游戏对象时,且脚本未被禁用时,执行1次
    //作用:初始化
    private void Start()
    {
        Debug.Log("Start()方法被调用了!");
    }

    //===================物理阶段===================
    //执行时机:每隔固定时间被调用,默认为0.02s(时间可以更改)
    //适用性:适合对物体做物理操作(移动,旋转等),不会受到渲染的影响。
    //因为每帧渲染物体量不固定机器性能不同,进而渲染的时间是不固定。
    //总之记住不管怎么样FixedUpdate()每隔0.02s执行一次就行
    private void FixedUpdate()
    {
        Debug.Log("FixedUpdate()方法被调用了!");
    }

    //===================游戏逻辑阶段===================
    //执行时机:每渲染一帧执行一次(联系FixedUpdate()对应理解),渲染受到机器性能个每帧渲染量影响,执行间隔时间不确定
    //适用性:处理游戏逻辑,但是现在大部分移动也是写这儿
    private void Update()
    {
        Debug.Log("Update()方法被调用了!");
    }

}

6-9.脚本生命周期图:

具体就是脚本的一些函数执行流程:

脚本生命周期

二、开发工具

1-MonoDevelop

MonoDevelop:Unity:Unity自带脚本编辑器,创建Mono应用程序,适用于Linux、Mac OS X和Windows的集成开发环境,支援C#、BOO和JavaScript等高级编程语言。

image-20210823224402999

2-Visual Studio(推荐)

微软公司的开发工具包,包括了整个软件生命周期中需要的大部分工具,如团队开发工具、集成开发环境等等。

3-修改默认开发工具

  • 在Unity中通过菜单设置修改默认的脚本编辑器:Edit一Preferences一External Tools一External Script Editor
  • 如安装了visual studio选择即可,以后在unity打开脚本自动启动vs打开

三、常用API

1-Unity核心类图

注意看图中的MonoBehaviour,一般我们是继承MonoBehaviour来编写脚本

核心类图

2-脚本的Component类

主要是获取游戏对象的组件,改变组件相关数值

2-1.操作unity编译器中的属性

主要是使用GetComponent()方法获取组件,然后在对组件的属性进行操作

  1. 创建一个脚本,并将脚本拖动到游戏对象上

image-20211208230124368

  1. 编辑脚本内容如下:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

/// <summary>
/// Component类提供了查找(在物体、后代、先辈)组件的功能
/// </summary>
public class ComponentDemo : MonoBehaviour
{
    //OnGUI()基本不使用,这里用于测试功能,会在运行时在屏幕上添加按钮
    private void OnGUI()
    {
        if (GUILayout.Button("修改位置"))
        {
            //改变当前游戏对象的位置,修改transform的position属性
            this.transform.position = new Vector3(0, 0, 10);
        }

        if (GUILayout.Button("修改BoxCollider大小"))
        {
            //获取游戏对象里边BoxCollider类型的组件,并将其size属性设置为1, 2, 3
            this.GetComponent<BoxCollider>().size = new Vector3(1, 2, 3);
        }

        if (GUILayout.Button("GetComponents"))
        {
            //获取游戏对象里边Component类型的组件
            var allComponents = this.GetComponents<Component>();
            //遍历出我们找到的组件
            foreach(var item in allComponents)
            {
                print("获取到的组件为:" + item.GetType());
            }
        }
    }
}
  1. 点击修改位置按钮后,物体的transform的position属性变为代码中设置值

image-20211208233025219

  1. 运行后查看,发现我们其实可以获取Inspector面板的组件信息,并加以修改

image-20211208232841454

2-2.获取游戏对象后代和先辈的组件

  1. 将脚本拖拽到游戏对象上,然后创建后代cube(后代cube无脚本)

image-20211209200315989

  1. 在脚本中添加:
if (GUILayout.Button("GetComponentsInChildren"))
{
    //获取当前游戏对象后代物体的里边MeshRenderer类型的组件(从自身开始找起,包括自己)
    var allComponents = this.GetComponentsInChildren<MeshRenderer>();
    //遍历出我们找到的组件
    foreach (var item in allComponents)
    {
        print("获取到后代的组件为:" + item.GetType());
    }

}

if (GUILayout.Button("GetComponentsInParent"))
{
    //获取当前游戏对象先辈物体的里边MeshRenderer类型的组件(从自身开始找起,包括自己)
    var allComponents = this.GetComponentsInParent<MeshRenderer>();
    //遍历出我们找到的组件
    foreach (var item in allComponents)
    {
        print("获取到后代的组件为:" + item.GetType());
    }

}
  1. 运行点击对应按钮,可以发现获取到了相应组件

image-20211209200543029

3-脚本的Transform类

主要是设置游戏对象物体的旋转缩放位置等等,可以制作游戏移动之类的操作

3-1.获取子对象变换组件

  1. 还是只在cube上设置脚本

image-20211209210703346

  1. 将下列内容写入脚本
if (GUILayout.Button("foreach-transform"))
{
    foreach (Transform child in this.transform)
    {
        //遍历出当前对象每个子物体的变换组件,孙子类就不行了
        print("获取到子物体的变换组件为:" + child.name);
        
        //物体相对与世界坐标系的位置
        //this.transform.position;

        //物体相对于父物体轴心点
        //this.transform.localPosition;

        //相对与父物体的缩放比例 1 2 1(xyz轴比例)
        //this.transform.localScale;


        //this.transform.lossyScale
        //如:父物体localScale为3当前物体localScale为2
        //lossyScale则为6

        //理解为:物体与模型缩放比例(自身缩放比例*父物体缩放比例)
        //this.transform.lossyScale
        //如:父物体localScale为3当前物体localScale为2
        //lossyScale则为6
    }
}
  1. 查看结果,只打印出子类的名字,子类的子类(孙子类)则不会打印。

image-20211209210904460

3-2.改变对象的坐标-1

  1. 还是脚本作用在cube上
  2. 添加脚本内容如下:
if (GUILayout.Button("z轴移动1m"))
{
    //向自身坐标系z轴,移动1m
    this.transform.Translate(0, 0, 1);
}

if (GUILayout.Button("y轴旋转1m"))
{
    //沿着自身坐标系y轴旋转10度
    this.transform.Rotate(0, 10, 0);
    //沿着世界坐标系y轴旋转10度
    this.transform.Rotate(0, 10, 0, Space.World);
}
  1. 点击之后可以发现物体移动

image-20211209211947576

3-3.改变对象的坐标-2

  1. 还是将脚本作用与cube,内容如下:
public class ComponentDemo : MonoBehaviour
{
    //传入一个物体的变换组件的引用,使得我们可以对其操作
    public Transform tf;
    private void OnGUI()
    { 
        if (GUILayout.Button("GetParent-Transform"))
        {
            //获取父物体的变换组件
            Transform parentTF =this.transform.parent;
        }

        if (GUILayout.Button("SetParent-true"))
        {
            //将传入的Transform设置当前对象的父物体
            //true:当前物体的位置视为世界坐标,参照物为世界坐标
            this.transform.SetParent(tf, true);
        }

        if (GUILayout.Button("SetParent-false"))
        {
            //将传入的Transform设置当前对象的父物体
            //false:当前物体的位置视为localPosition,参照物为父物体轴心点
            this.transform.SetParent(tf, false);
        }
    }
}
  1. 查看新增一个Tf变量,这里Tf可以接收一个Transform

image-20211209222221022

  1. 我们新建一个cube(6)传入它的Transform给cube,类似于(public Transform tf=“cube的Transform组件”)

image-20211209222041008

  1. 运行,然后点击SetParent-true按钮,然后改变cube(6)的位置,发现cube和cube(6)做相对运动(其实这就是正常的子物体与父物体关系)。类比载具,相当于cube上了名为cube(6)的车。cube就跟着父物体cube(6)一起走了。
  2. 运行,然后点击SetParent-false按钮,会发现cube的坐标会直接变为cube(6)的坐标(即重叠了),然后拖动cube(6),发现cube不会再移动。查看cube的坐标为(0,0,0),查看cube(6)的坐标为(0,2,0),所以就是cube将cube(6)的位置作为了参照物而已

image-20211209224343595

image-20211209224403009

  1. 总结:SetParent-true就是正常的父物体与子物体的关系,子物体跟着父物体移动,无需记忆;SetParent-false则是将子物体的Transform的参照点改变了,原本物体创建都是默认将世界坐标作为参照点,这里是直接将父物体的坐标设置为了参照点而已。

3-4.查找子物体的Transform

if (GUILayout.Button("FindChildTransform"))
{
    //根据名称寻找子物体的Transform
    Transform childTF=this.transform.Find("cube(1)");
    //根据索引寻找子物体的Transform
    //Transform childTF = this.transform.GetChild(0);
}

4-GameObject类

4-1.激活状态

  1. 激活状态对应到Inspector面板中的勾选框

image-20211210212500801

  1. 相关脚本如下:
public class GameObjectDemo : MonoBehaviour
{
    private void OnGUI()
    {
        //获取在场景中物体激活状态(物体实际激活状态)
        //this.gameObject.activeInHierarchy
        
        //获取物体激活状态,激活也有可能不显示。例如:父物体取消激活,子物体就算激活了也显示不出来
        //this.gameObject.activeSelf
        
        //设置物体的激活状态
        //this.gameObject.SetActive()
    }
}

4-2.创建GameObject

  1. 脚本内容如下:
public class GameObjectDemo : MonoBehaviour
{
    private void OnGUI()
    {
        if (GUILayout.Button("添加光源组件"))
        {
            //创建物体
            GameObject lightGo = new GameObject();
            //添加组件
            Light light=lightGo.AddComponent<Light>();
            //设置光的颜色
            light.color=Color.red;
            //设置光的类型
            light.type = LightType.Point;
        }
    }
}
  1. 将脚本拖拽到cube运行,点击按钮后,Hierarchy面板创建了一个GameObject正好是我们定义的光组件

image-20211210214058451

下:

public class GameObjectDemo : MonoBehaviour
{
    private void OnGUI()
    {
        if (GUILayout.Button("添加光源组件"))
        {
            //创建物体
            GameObject lightGo = new GameObject();
            //添加组件
            Light light=lightGo.AddComponent<Light>();
            //设置光的颜色
            light.color=Color.red;
            //设置光的类型
            light.type = LightType.Point;
        }
    }
}
  1. 将脚本拖拽到cube运行,点击按钮后,Hierarchy面板创建了一个GameObject正好是我们定义的光组件

image-20211210214058452

四、练习

1-修改血量最低的敌人的组件属性

  1. 创建名为Enemy的脚本
public class Enemy : MonoBehaviour
{
    //血量
    public float HP;
}
  1. 创建名为FindEnemyDemo的脚本
public class FindEnemyDemo : MonoBehaviour
{
    private void OnGUI()
    {
        if (GUILayout.Button("查找血量最低的敌人"))
        {
            //寻找所有含有Enemy类型的引用
            Enemy[] Enemies = FindObjectsOfType<Enemy>();
            //找出血量最低的游戏对象的引用
            Enemy min = FindEnemyByMinHP(Enemies);
            //获取血量最低的敌人的Transform组件
            Transform minTransform = min.GetComponent<Transform>();
            //将血量最低的敌人positon设置为3,3,3
            minTransform.position = new Vector3(3, 3, 3);
        }
    }

    public Enemy FindEnemyByMinHP(Enemy[] Enemies)
    {
        //假设第一个就是血量最低的敌人
        Enemy min = Enemies[0];
        //依次与后面比较
        for (int i = 1; i < Enemies.Length; i++)
        {
            if (min.HP > Enemies[i].HP)
            {
                min = Enemies[i];
            }
        }
        return min;
    }
}
  1. 在hierachy面板中创建6个cube,并将Enemy挂到6个cube上。然后给cube中的HP赋值。这样我们就可以开始找血量最低的游戏对象了

请添加图片描述

  1. 将名为FindEnemyDemo的脚本挂到Main Camera上,然后运行
    请添加图片描述

  2. 点击按钮,发现血量最少的cube的positon变为了3,3,3。
    请添加图片描述

2-变换组件查找工具类

public class TransformHelper : MonoBehaviour
{
    /// <summary>
    /// 在层级位置的情况下查找子物体
    /// </summary>
    /// <param name="parentTF"></param>
    /// <param name="childName">子物体名称</param>
    /// <returns></returns>
    public static Transform GetChild(Transform parentTF, String childName){
        //在子物体中查找
        Transform childTF=parentTF.Find(childName);
        //找到了返回引用
        if (childTF!=null){
            return childTF;
        }
        //将问题交给子物体
        int count = parentTF.childCount;
        for (int i = 0; i < count; i++){
            childTF = GetChild(parentTF.GetChild(0), childName);
            //找到了返回引用
            if (childTF!=null){
                return childTF;
            }
        }
        //以上都没找到,就返回null
        return null;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值