📢前言
- 如果你点进来了,那么恭喜你看到了一篇关于Unity中的脚本超级详细的全面介绍🔔
- 内容干不干,往下看就知道了!🎄
- 本篇博客来简单介绍一下Unity组件中很重要的一种——脚本组件的介绍 🎅
- 几乎覆盖了关于脚本内容的大部分,希望感兴趣的小伙伴可以认真看完哦🎉
- 如果看完学不会请顺着网线来咬我🙊!
❤️脚本
-
首先让我们回顾一下上一篇文章中对Unity中的脚本的简单介绍
-
脚本在Unity中也是一种组件,在之前的组件介绍文章中已经说过
-
脚本必须要继承MonoBehaviour,而且文件名要和类名一致(如果在Unity里更改了C#文件的名称,在这个脚本里也要改类名),不然脚本挂不上游戏对象。
🧡脚本概念
- 脚本是附加在游戏物体上用于定义游戏对象行为的指令代码
- 脚本脚本就是Unity中写代码的东西!(🙉小白提醒~)
- Unity支持三种高级编程语言:
C#、JavaScript 和 Boo Script(unity4以前支持的)
💛开发工具
开发工具指的是需要用来写代码,写脚本的东西!(🙈小白提醒~)
MonoDevelop
- unity自带的脚本编辑器,创建Mono应用程序,适用于Linux、Mac OSX和Windows的集成开发环境,支持C#和JavaScript等
- 这款编辑器应该是Unity5.0或者是之前的老版本使用的,目前是被淘汰了用不到
Visual Studio
- 微软公司的开发工具包,包括了整个软件生命周期中需要的大部分工具,如团队开发工具,集成开发环境等等。
- 在Unity中通过菜单设置修改默认的脚本编辑器,Visual Studio 官网下载链接在这
JetBrains Rider
- JetBrains Rider 是一款基于 IntelliJ 平台和 ReSharper 的跨平台 .NET IDE。
- Rider 支持 .NET 框架、新的跨平台框架 .NET Core 和基于 Mono 的项目。
- 这使您可以开发广泛的应用程序,包括:.NET 桌面应用程序、服务和库、Unity 游戏、Xamarin 应用、ASP.NET 和 ASP.NET Core web 应用程序。
- JetBrains Rider官网下载链接在这,感兴趣的可以下载试试,跟VS开发差不多
💚脚本结构介绍
- 在这里说一下Monobehaviour的继承关系
- 脚本—>Monobehaviour—>Behaviour—>Component—>Object
- 下面是一个普通脚本的结构介绍
using 命名空间;
public class 类名:MonoBehaviour
{
void 方法名()
{
Debug.Log("调试显示信息");
print("本质就是Debug.Log方法");
}
}
- 下面是新建一个脚本打开的样子,Start() 方法和 Update() 方法是Unity中新建一个脚本时自动就有的方法
- Start() 方法在程序开启的第一帧执行
- Update() 方法在程序的每一帧都会执行
- Unity中每一帧是0.02秒
创建脚本有好几种方法,这里简单介绍两种
1.Project面板右键-> Create-> C# Script
2.在游戏对象上点击Add Component,直接输入想要创建的脚本名称就可以了
这样脚本会自动创建完成并且挂载到刚才点击的游戏对象上面
💙脚本的生命周期
-
说道脚本,自然要谈一谈Unity中的脚本生命周期啦。
-
因为我们所写的代码都要有一个执行顺序,才能有条不紊的执行我们需要让程序执行的事情,听起来还有些绕口~
-
类似Android中的Activity也有自己的生命周期
-
先来看一张挺长的图,这张图几乎涵盖了脚本的所有方法执行的顺序啦~
看完了图是不是感觉有点慌,有的小伙伴就要说啦:这个图的方法那么多,看着就头大啦,看不明白~
那不要担心,我们今天主要讲的是Unity脚本生命周期的九大回调,也是脚本中最常用到的方法了
想让回调函数执行有个前提,就是 脚本必须以组件的方式挂载到场景中某一个游戏对象身上
- 九大回调说的就是就是下面图中这九个方法啦
- 三种Update都是每帧执行一次
- FixedUpdate一般用于表现物理效果的时候使用会比Update效果好
- LateUpdate在相机脚本上使用较好,因为相机可以在Update执行之后运行,不会出现画面Bug的现象
其中他们的执行顺序:
Awake——>OnEnable——>Start——>FixedUpdate——>Update——>LateUpdate——>OnGUI——>OnDisable——>OnDestroy
- 如果有多个脚本中同时在Awake()或Start()中写了方法,哪一个脚本中的先执行呢?
这个问题需要考虑,不然的话代码中会出现一个时效出错,空引用之类的错误
这里告诉大家一个办法,就是下面这套流程。
可以在下图面板上将脚本的先后顺序添加上,程序就会按照我们设置的顺序来执行啦
如果没有设置这个,那不同脚本先后执行速度就是随机的了,不过Awake和Start也都是在第一帧执行的
- Edit->Project Settings->Script Execution Order
💜调试方法
Unity编辑器调试
在脚本代码中用打印方法调试
Debug.Log()或者print()进行打印log调试
使用Visual Studio调试
调试步骤:
- 在可能出错的行添加断点
- 启动调试
- 在Unity中Play场景
- 在vs中按F11逐条调试
- 调试完毕按F5退出调试
也可以在调试时右键–>快速监视,在快速监视面板便捷的调试和查看数据。
还可以在即时窗口输入代码进行调试
因为Update和其他方法不同,它是逐帧运行的,所以在调试时需要单帧调试
- 步骤:启动调试->运行场景->暂停游戏->加断点->单帧执行->结束
🖤Unity脚本常用的API
阅读编程资料时经常会看到API这个名词,网上各种高大上的解释估计放倒了一批初学者。
API(Application Programming Interface,应用程序编程接口)是一些预先定义的函数,目的是提供应用程序与开发人员基于某软件或硬件得以访问一组例程的能力,而又无需访问源码,或理解内部工作机制的细节。——百度百科
网上关于API的介绍有一大堆,大多数还举了例子介绍
其实把API看成一个使用文档就得了~哪来这么多事
既然介绍完了脚本,那就顺带介绍几种脚本常用的API吧,可以先来看一下Unity的核心类图(图片上网上我能找到的最清晰的了,虽然还是有些模糊~)
GameObject(游戏对象)
GameObject游戏对象:GameObject提供了添加、设置游戏对象,查找、销毁游戏对象的功能
下图是GameObject的属性参数。
下面使用代码示例来学习一下
void Start()
{
Debug.Log(gameObject.activeSelf);//当前游戏对象的激活状态
gameObject.SetActive(false);//设置当前对象的激活状态
gameObject.SetActive(!gameObject.activeSelf);//设置当前游戏对象的激活状态为当前状态的反向状态
Debug.Log(gameObject.name);//获取当前对象名字
Debug.Log(gameObject.tag);//获取当前对象标签
Debug.Log(gameObject.layer);//获取当前对象层
Light mylight = gameObject.GetComponent(type:"Light") as Light;
mylight = gameObject.GetComponent(typeof(Light))as Light;
mylight = gameObject.GetComponent<Light>();//获取当前游戏对象的组件
mylight = gameObject.AddComponent<Light>();//添加一个组件到游戏对象身上,并返回这个组件
---
GameObject lt = GameObject.Find("Directional Light");//通过名字找到单个游戏对象
GameObject com = GameObject.FindWithTag("Player");//通过标签找到单个游戏对象
com = GameObect.FindGameObjectWithTag("MainCanmer");
GameObject[] coms = GameObject.FindGameObjectWithTag(MainCanmer);//通过标签找到多个游戏对象
}
特别要注意的是不同通过Find找到处于未激活状态的对象
Inspector面板上这里未勾选就是未激活的意思,相应的在Hierarcht面板上的游戏对象就会变成灰色。
Component(组件)
Component就是组件,在上篇博客已经介绍了,这里再重新介绍下脚本API的使用吧
添加、获取和销毁组件的方法
添加组件
GameObject Cube= GameObject.CreatePrimitive(PrimitiveType.Cube);//创建一个方格
Cube.AddComponent<BoxCollider>();//添加盒形碰撞器组件
Cube.AddComponent<Rigidbody>();//添加刚体组件
Cube.AddComponent<Test>();//添加Test脚本
获取组件
BoxCollider boxCollider = Cube.GetComponent<BoxCollider>();//获取盒形碰撞器组件
Rigidbody rigidbody = Cube.GetComponent<Rigidbody>();//获取刚体组件
Test test= Cube.GetComponent<Test>();//获取某个Test脚本
GetComponent
GetComponents
GetComponentInChildren
GetComponentsInChildren
GetComponentInParent
GetComponentsInParent
销毁组件
//()中的参数为创建相应组件时的组件名称
Destroy(boxCollider );//销毁盒形碰撞器组件
Destroy(rigidbody);//销毁刚体组件
Transform
Transform组件在上篇介绍组件的博客也说过了,这里在提一下在脚本使用的API
下面来用代码示例学一下Transform的使用
void Start(){
//1.控制游戏对象的变换
Debug.Log(transform.position);//世界坐标
Debug.Log(ransform.localPostion);//本地坐标
Debug.Log(transform.eulerAngles);//世界欧拉角
Debug.Log(transform.localEulerAngles);//本地欧拉角
Debug.Log(transform.rotation);//世界旋转(四元数)
Debug.Log(transform.localRotation);//本地旋转(四元数)
//本地缩放
Debug.Log(transform.localScale);
//方向
//自身前方的方向向量
Debug.Log(transform.forward);
//自身右方的方向向量
Debug.Log(transform.right);
//自身上方的方向向量
Debug.Log(transform.up);
//描述游戏对象的层级关系
//父对象
Debug.Log(transform.parent);
//根对象
Debug.Log(transform.root);
//设置父物体
transform.parent = lookAtTarget;
//==>
transform.SetParent(lookAtTarget);
//遍历所有的子对象
foreach (Transform tra in transform)
{
Debug.Log(tra);
}
//遍历所有的子对象
for (int i = 0; i < transform.childCount; i++)
{
Debug.Log(transform.GetChild(i));
}
//找当前对象的子对象
Transform sph = transform.Find("Cylinder/Sphere");
Debug.Log(sph);
}
private void Update()
{
//角色朝自身前方移动
// transform.position += transform.forward * 0.05f;
// transform.Translate(transform.forward * 0.05f);
//自转
transform.Rotate(new Vector3(0,1,0),2,Space.World)
// ==>
transform.eulerAngles += new Vector3(0,1,0);
transform.Rotate(new Vector3(0,1,0),2,Space.Self);
//==>
transform.localEulerAngles += new Vector3(0,1,0);
//绕某个点沿某个轴,旋转
transform.RotateAround(new Vector3(0, 0, 0), new Vector3(0, 1, 0), 5);
transform.LookAt(new Vector3(0,0,0));
transform.LookAt(lookAtTarget);//看向某个对象
Vector3(三维向量)
Vector3向量,在三维坐标系中带有方向和大小的数据,下图介绍了Vector3的属性参数
下面来用代码示例学一下 Vector3的使用
//创建一个三维向量
Vector3 dir = new Vector3(1,2,3);
//创建一个二维向量
Vector2 dir2 = new Vector2(3,3);
//创建一个四维向量
Vector4 dir4 = new Vector4(1,2,3,4);
//获取一个向量的单位向量
Vector3 normalDir = dir.normalized;
//将当前向量变成单位向量
dir.Normalize();
//向量的长度【模】
float mag = dir.magnitude;
//模的平方【用来做向量长度的对比】
float sqrMag = dir.sqrMagnitude;
// Vector3.forward
// Vector3.back
// Vector3.left
// Vector3.right
// Vector3.up
// Vector3.down
// Vector3.zero
// Vector3.one
Vector3 pointA = Vector3.forward;
Vector3 pointB = Vector3.right;
//求两个坐标的距离
float dis = Vector3.Distance(pointA, pointB);
Vector3 dirA = Vector3.one;
Vector3 dirB = Vector3.right;
//求两个向量的夹角
float angle = Vector3.Angle(dirB, dirA);
float newAngle = Vector3.Angle(new Vector3(1, 1, 0), Vector3.zero);
//求两个向量的点乘
float dot = Vector3.Dot(dirA, dirB);
//求两个向量的叉乘【求两个向量的法向量】
Vector3 normal = Vector3.Cross(dirA, dirB);
//求一个向量在某个方向上的投影向量
Vector3 pro = Vector3.Project(new Vector3(1, 1, 0), Vector3.right);
//求两个向量中间的插值坐标
Vector3.Lerp(new Vector3(1, 0, 0), new Vector3(5, 0, 0), 0.5f);
//由快到慢运动过去
transform.position = Vector3.Lerp(transform.position, new Vector3(5, 0, 0), 0.03f);
Quaternion(四元数旋转)
Quaternion四元数本质上是一种高阶复数,是一个四维空间,相对于复数的二维空间。
在Unity里,tranform组件有一个变量名为Rotation,它的类型就是四元数。用于在Unity中控制旋转的一种方式
下面来用代码示例学一下 Quaternion的使用
public class ReadQuaternion : MonoBehaviour
{
public float turnSpeed = 3f;
public Transform target;
void Start()
{
//空旋转【相当于欧拉角的(0,0,0)】
Debug.Log(Quaternion.identity);
//将当前角色的旋转设置空旋转
transform.rotation = Quaternion.identity;
}
void Update()
{
// transform.LookAt(target);
//插值旋转
// transform.rotation 起点四元数
//玩家指向敌人的方向向量
Vector3 dir = target.position - transform.position;
//目标四元数
Quaternion targetQua = Quaternion.LookRotation(dir);
// transform.position = Vector3.Lerp(transform.position,Vector3.right*5,0.02f);
//插值转身
transform.rotation = Quaternion.Lerp(transform.rotation, targetQua, 0.02f * turnSpeed);
}
}
Time(时间)
首先介绍两个概念:现实时间和游戏时间
大多数Time类都是依赖于游戏时间的。
现实时间也就是不依赖于程序内部,就算程序暂停也会继续计算的真实时间,而游戏时间是基于程序内部的,可以自行调整。
我们这里用的都是游戏时间
下面来用代码示例学一下 Time 类的使用
public class ReadTimeAndMathf : MonoBehaviour
{
//时间缩放
public float timeScale = 1f;
public float moveSpeed = 2;
private void FixedUpdate()
{
Debug.Log(Time.fixedDeltaTime);
transform.position += Vector3.forward * Time.fixedDeltaTime * moveSpeed;
}
void Update()
{
// Debug.Log(Time.time);//游戏过来多长时间
Debug.Log(Time.deltaTime);//每帧的时间间隔
//调整时间缩放
Time.timeScale = timeScale;
// transform.position += Vector3.forward * Time.deltaTime * moveSpeed;
// transform.position = Vector3.Lerp(transform.position,Vector3.forward * 5 ,Time.deltaTime * moveSpeed);
}
}
Input键盘输入方法
在游戏中我们经常要用到按某个键来执行某件事,就比如按A键开炮,空格键跳跃等等。下面就来简单介绍一下怎样使用 键盘输入方法
public class SimplePlayerMove : MonoBehaviour
{
[Header("炮弹")]
public GameObject bullet;
public float moveSpeed = 3f;
public float turnSpeed = 3f;
private float hor, ver;
void OldUpdate(){
bool downA = Input.GetKeyDown(KeyCode.A);
if (Input.GetKeyDown(KeyCode.A))
{
Debug.Log("按下了A键");
}
if (Input.GetKeyUp(KeyCode.A))
{
Debug.Log("松开了A键");
}
if (Input.GetKey(KeyCode.Space))
{
Debug.Log("按住了空格键");
}
if(Input.GetKey(KeyCode.W))//前进
{
transform.position +=transform.forward * Time.deltaTime * moveSpeed;
}
if(Input.GetKey(KeyCode.S))//后退
{
transform.position -=transform.forward * Time.deltaTime * moveSpeed;
}
if (Input.GetKey(KeyCode.A))//左转
{
transform.eulerAngles -= Vector3.up * turnSpeed;
}
if (Input.GetKey(KeyCode.D))//右转
{
transform.eulerAngles += Vector3.up * turnSpeed;
}
}
----
void Update()
{
hor = Input.GetAxis("Horizontal");
ver = Input.GetAxis("Vertical");
//transform.position += new Vector3(hor, 0, ver) *Time.deltaTime * moveSpeed;
//前后移动
transform.position += ver * transform.forward * Time.deltaTime * moveSpeed;
//左右转身
transform.eulerAngles += hor * Vector3.up * turnSpeed;
}
}
Input鼠标输入方法
说完了键盘输入,自然还有鼠标输入啦,那下面就来介绍一下 鼠标输入方法
void KeyUpdate()
{
if (Input.GetMouseButtonDown(0))
{
Debug.Log("按下了鼠标左键");
}
if (Input.GetMouseButtonUp(0))
{
Debug.Log("松开了鼠标左键");
}
if (Input.GetMouseButton(0))
{
Debug.Log("按住了鼠标左键");
}
}
---
void Update()
{
float hor = Input.GetAxis("Horizontal");
//Debug。Log( if (Input.GetButtonDown("Fire"));
{
Debug.Log("按住了开火键");
// GameObject crtPlayer = Instantiate(playerPrefab);
GameObject crtPlayer = Instantiate(playerPrefab, Vector3.forward, Quaternion.identity);
})
if (Input.GetButtonDown("Fire"))
{
Debug.Log("按住了开火键");
// GameObject crtPlayer = Instantiate(playerPrefab);
GameObject crtPlayer = Instantiate(playerPrefab, Vector3.forward, Quaternion.identity);
}
}
💞Unity实现功能的三部曲
- 找到要操作的游戏对象或他的组件
- 找到功能所对应的组件
- 设置组件的属性,或调用组件提供的方法,从而实现功能
注意
- 当程序出现异常,程序会自动暂停,后边的代码就不会执行了
- 游戏运行时,所有的操作在停止游戏后,都会恢复
- 要真的去做某项操作时,不要在游戏运行时操作
👥总结
- 好了,关于脚本方面呢我们已经全部介绍完了,最后还简单提了一下Unity实现功能的三部曲
大家都学会了吧,不用顺着网线来咬我了~
- 其实整个游戏程序实现功能的思路就是这样,先找到这个游戏对象,然后利用脚本写代码让他做相应的事情。
- 所有的游戏对象有条不紊的执行他们该干的事情,那么一个游戏程序也就自然而然的运行起来了
- 如果你是从之前写的几篇博客完整看到这里,那么我之前写的那篇坦克大战的小游戏也就可以自己做了
Unity中从倔强青铜上荣耀王者,看完这篇是不是有手就行~
- 其实做一个小游戏也没有那么难嘛~嘿嘿
- 不过关于Unity中UI方面还没有介绍,大家可以在网上搜一下大佬的文章看看呀,后续有时间会更UI方面的知识点的~
下面将几篇关于Unity的使用文章挂在这里啦,对Unity有兴趣的小伙伴可以再看一看哦,可以自己动手做一个小游戏玩~
- 💝💘💖Unity ❉ 基础知识 ☀️| 一起走进游戏引擎界大佬——Unity 的陈情往事 (^_−)☆
- 💗💝❤️Unity ❉ 基础知识 ☀️| 轻松学会 Unity界面布局和简单实例——入门级!(^_−)☆
- 💓💝💘Unity ❉ 基础知识 ☀️| 学会这些 Unity实用知识点,我也可以做小游戏啦!(^_−)☆
- 💖💗💝Unity ❉ 基础知识 ☀️| 学会这些 Unity常用组件,我离开发⭐️3A游戏大作⭐️又近了一步!(^_−)☆
如果上面这几篇博客内容都差不多了,就可以自己动手做下面小游戏试试啦~🎅下面文章教程和源码都在,可以简单参考下哦
心动不如行动,还不赶紧一键三连,然后收藏夹吃灰?
祝大家早日上荣耀王者!
下篇再见啦~さようなら
转载:https://blog.csdn.net/zhangay1998/article/details/117318835