脚本参数
参数的基本用法
脚本的参数,用于控制脚本组件的功能
修改 SimpleLogic.cs ,在SimpleLogic类中添加一个参数
1 | public float rotateSpeed = 30f; |
参数的用法:
参数必须为 public ,才可以在检查器中显示
参数的名称,即变量名。如变量名
rotateSpeed
会自动在检查器中更新为Rotate Speed
参数的默认值,即变量的默认值
可以用 Reset 菜单重置参数的工具提示(注解),可以用
[Tooltip("comment")]
指定1
[ Tooltip("这个参数是角速度") ]
参数的赋值
定义默认值
1
public float rotateSpeed = 30f ; // 初始化参数,并设置默认值
在检查器中赋值
1
script.rotateSpeed = 180f; //由 Unity 框架对参数赋值
在 Awake 中初始化
- 在 Start 中初始化
注意:start()方法最后执行,所以最终以start()方法中的设定值为准。
值类型与引用类型
参数的类型,分为 值类型、引用类型
- 值类型:如 int , float , bool, string
- 值类型 (struct) :如 Vector3 ,Color
- 引用类型 (class) :如 GameObject,Transform ,MeshRenderer
值类型的特点:
- 本身是一个值,可直接赋值
- 若未赋值,则默认为0
- 不能为 null
结构体 struct 类型是 C# 中的特殊写法,也是值类型
- 若未赋值,则各字段值为 0
- 如果要设默认值,必须用new关键字赋值(并非创建对象)
- 不能设为 null
定义值类型
1 | public int intValue = 0; |
检查器中的属性:
注意:
- 在C#中,int, float 等基本类型在本质上也是 struct 类型
- string ,原则上属于 class 类型
引用类型包括:
- 节点 ,GameObject
- 组件 ,如 Transform、MeshRenderer 、AudioSource ..
- 资源 ,如 Material 、Texture 、AudioClip 、…
- 数组类型
例子:火车和红旗
新建小火车,红旗1两个物体
小火车挂载TrainLogic
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28public class TrainLogic : MonoBehaviour
{
public GameObject target; // 目标物体
// 注意,target 必须在检查器里赋值 !!!
// 若 targe 未赋值,在控制台里会报告异常 UnassignedReferenceException
void Start()
{
// 近似60FPS运行,不需要太快,不然CPU卡
Application.targetFrameRate = 60;
this.transform.LookAt(target.transform);
}
void Update()
{
Vector3 p1 = this.transform.position;
Vector3 p2 = target.transform.position;
Vector3 p = p2 - p1;
float distance = p.magnitude;
if( distance > 0.3f )
{
float speed = 2;
float move = speed * Time.deltaTime;
this.transform.Translate(0, 0, move, Space.Self);
}
}
}
3.此时把红旗1拖动到检查器TrainLogic的Target选项中,即可实现对引用类型变量赋值
运行时调试
在游戏运行时,可以对 物体 / 组件 进行实时调试
在运行模式下,所有参数不能保存到场景
保存参数的办法:
在 Play Mode 下,暂停游戏,inspector-组件-Copy Component
停止游戏,在 Edit Mode 下,inspector-组件-Paste Component Values即可将调试合适的参数值设置到检查器中
外部设备输入
鼠标输入
游戏的输入,可以来自鼠标、键盘、触摸屏、游戏手柄等
鼠标输入 相关API :
- 按下鼠标:Input.GetMouseButtonDown()
- 抬起鼠标:Input.GetMouseButtonUp()
- 当前是否按住鼠标:Input.GetMouseButton()
例如,鼠标按下事件
1 | if(Input.GetMouseButtonDown( 0 )){ |
其中,0 左键 / 1 右键 / 2 中键
例子:实现鼠标按下时,风扇旋转;鼠标松开时,风扇停止
创建风扇,挂载FanLogic
1 | public class FanLogic : MonoBehaviour |
注意:
- 鼠标事件只触发一次,不会重复触发
- 鼠标应该在 Game 窗口里点击,不是 Scene窗口
事件探测 & 状态探测
- 区分 事件探测 & 状态探测
鼠标事件探测,只触发一次Input.GetMouseButtonDown()
Input.GetMouseButtonUp()
鼠标状态探测,表示当前是否正在被按下Input.GetMouseButton()
- 鼠标事件是全局的,每个脚本互不影响
屏幕坐标
鼠标按下时,取得鼠标的当前所在位置:Input.mousePosition
1 | if(Input.GetMouseButtonDown( 0 )) |
其中,Input.mousePosition
是鼠标在屏幕上的坐标
屏幕坐标:
左下角为 (0, 0),宽度 Screen.width
,高度 Screen.height
,坐标单位是 像素
一个物体,在屏幕上的位置 :
worldPos = this.transform.position;
把worldPos世界坐标转化为screenPos屏幕坐标:
screenPos = Camera.main.WorldToScreenPoint(worldPos);
其中所谓屏幕,是相对于摄像机而言的,实际上是指摄像机拍出来的屏
例子:物体运动时,检查是否超出屏幕的边界
创建Cube,挂载CubeLogic.cs
1 | public class CubeLogic : MonoBehaviour |
键盘输入
获取键盘输入,
相关 API :Input. GetKeyDown (key)
按键事件探测,按下Input. GetKeyUp (key)
按键事件探测, 抬起Input. GetKey (key )
按键状态探测,是否正被按下
例子:当按下 W 键时,飞行器向前运动 。
FlyLogic.cs
1 | public class FlyLogic : MonoBehaviour |
键值常量,可参考官方文档,如:
KeyCode.A ,表示A键
KeyCode.Space ,表示空格键
KeyCode.LeftArrow ,表示左箭头 …
注意:运动游戏时,鼠标点一下 Game 窗口,才能获得输入焦点
组件的访问
组件的调用
组件 Component ,代表一个功能。
例如,AudioSource 组件可用于播放音乐、音效
AudioSource的属性中, Play on Awake 属性表示自动播放
在代码中,也可以用 API 来使其播放音乐。
- 获取 AudioSource 组件
AudioSource audio = this.GetComponent<AudioSource>();
- 播放
audio.Play() ;
例子:鼠标单击时Cube开始播放音乐,再次单击停止播放音乐。CubeLogic.cs关键代码:
1 | void Update() |
此时,不勾选Play on Awake,可实现鼠标单击后播放音乐,再次单击则停止播放
注意:
<> 表示 泛型 ,上述例子中是获取
类型的组件 组件的上下排列顺序,对功能没有影响,可以手动 Move Up / Down 进行调整
组件的参数
组件的参数,也可以在代码中访问。几个常用参数如下:
- AudioClip ,音频资源
- Mute ,是否静音
- Loop ,是否循环播放
- Volume,音量
可参考API文档中的AudioSource类,比如静音
1 | AudioSource audio = this.GetComponent<AudioSource>(); |
引用其他组件
在脚本中,也可以引用其他物体下的组件。
第1种办法:
- 创建主控节点,背景音乐节点两个空节点
- MainLogic.cs
1 | // 初始化节点 |
主控挂载MainLogic.cs, 背景音乐挂载AudioSource组件
在主控节点的Bgm Node属性中挂载背景音乐节点
第2种办法:直接引用。此方法是第1种方法的简化。可直接在检查中赋值。
主控节点MainLogic.cs
1 | // 初始化AudioSource组件 |
总结:
- 获取当前物体下的组件,
comp = this.getComponent<T>
- 获取别的物体下的组件,
comp = otherNode.getComponent<T>
或者初始化属性后直接引用,无需初始化节点后再从节点种获取组件
引用脚本组件
一个脚本里,访问另一个脚本组件。和上一节中引用其他组件的方法类似,有两种方法,推荐第二种。
- API 获取
FanLogic fan = node.getComponent<FanLogic>();
- 直接引用
public FanLogic fan;
注意:脚本组件,和 Unity 自带的组件没有太大差别
消息调用
消息调用 SendMessage ,以“消息”的形式来调用另一个组件。
如上节中的MainLogic.cs:
1 | // 找到目标节点 |
FanLogic.cs 中增加方法 DoRotate():
1 | public void DoRotate() { |
SendMessage 的内部执行 ( 反射机制 ) :
- 找到 target 节点下的所有脚本组件 ( MonoBehaviour )
在组件下寻找 methodName 这个函数
- 若存在此函数,则调用它
- 若不存在,则继续查找
- 若最终无法匹配,则报错
SendMessage,并非‘消息’,其本质是同步调用
此方法不常用
综合练习
实现添加无人机,键盘WS按键控制升降,只在0~4m范围内活动
主控节点挂载MainLogic.cs
1 | public class MainLogic : MonoBehaviour |
无人机挂载FlyLogic.cs
1 | public class FlyLogic : MonoBehaviour |
旋翼挂载RotateLogic.cs
1 | public class RotateLogic : MonoBehaviour |
物体的访问
获取物体
游戏物体 GameObject ,也可以叫 节点
按 名称 / 路径 获取 ( 不推荐 )
1
2
3
4// 若不重名,可以按名称获取
GameObject node = GameObject.Find("旋翼");
// 最好指定全路径(如果有父子关系)
GameObject node = GameObject.Find("无人机/旋翼");引用获取
添加一个变量,在检查器中引用目标1
2
3
4
5
6public RotateLogic rotateLogic;
void Start()
{
rotateLogic.DoRotate();
}
注意:不建议使用 GameObject.Find() ,因为
- 执行效率低
- 不能自适应变化,当目标节点改名时会出错
父子物体
场景中的层级关系 / 父子关系,是由 Transform 组件维持的
获取父级,Transform parent = this.transform.parent;
获取父级节点,GameObject parentNode = this.transform.parent.gameObject;
可以发现,在 Unity 编程中 GameObject 很没有存在感。
获取子级,有几种方式。
foreach 遍历
1
2
3
4
5// 该方法只打印所有子节点,不会打印二级子级(子节点的子节点)
foreach (Transform child in transform)
{
Debug.Log("* 子物体: " + child.name);
}
GetChild() ,按索引获取。例如获取第 0 个子项,
Transform aa = this.transform.GetChild(0);
transform.Find() ,按名称查找子项
Transform aa = this.transform.Find("aa");
Transform bb = this.transform.Find("bb");
Transform cc = this.transform.Find("bb/cc");
注意:二级子级应该指定路径,如 bb/cc,否则找不到
物体的操作
设置新的父级this.transform.SetParent( newParentNode );
当 newParentNode 传入null时,设为一级节点this.transform.SetParent( null );
其中, parent 为 null 表示一级节点 ( 没有父级 )
GameObject.setActive() ,显示 / 隐藏。例如,
1 | Transform child = this.transform.Find("aa"); |
注意:transform.Find (“/222”) ,其中 / 表示在根(场景节点)下查找物体
练习
3D版的俄罗斯方块,按 空格键 切换形状
结构及脚本
PlayerLogic.cs
1 | public class PlayerLogic : MonoBehaviour |