学习视频:https://www.bilibili.com/video/BV15W411976h?p=13
Input.GetAxis
塔防项目代码示例
float h = Input.GetAxis("Horizontal");
float v = Input.GetAxis("Vertical");
float mouse = Input.GetAxis("Mouse ScrollWheel");
“Horizontal”、“Vertical”、"Mouse ScrollWheel"为项目设置,可在Edit > Project Settings > Input进行更改
Vector3
Vector3.up;
表示世界坐标系中 Y 轴正方向上的单位向量
用于编写 Vector3(0, 1, 0) 的简便方法。
Vector3.down;
表示世界坐标系中 Y 轴负方向上的单位向量
用于编写 Vector3(0, -1, 0) 的简便方法。
Vector3.left;
表示世界坐标系中 X 轴负方向上的单位向量
用于编写 Vector3(-1, 0, 0) 的简便方法。
Vector3.right;
表示世界坐标系中 X 轴正方向上的单位向量
用于编写 Vector3(1, 0, 0) 的简便方法。
Vector3.forward;
表示世界坐标系中 Z 轴正方向上的单位向量
用于编写 Vector3(0, 0, 1) 的简便方法。
Vector3.back;
表示世界坐标系中 Z 轴负方向上的单位向量
用于编写 Vector3(0, 0, -1) 的简便方法。
Vector3.magnitude
返回该向量的长度。计算方法:(xx+yy+z*z) 的平方根。
Vector3.sqrMagnitude 返回该向量的平方长度。计算方法:(xx+yy+z*z)
如果只需要比较一些向量的大小, 则可以使用 Vector3.sqrMagnitude 比较它们的平方数(省略开平方操作)
(Vector3 value).normalized
塔防项目代码示例
transform.Translate((positions[index].position - transform.position).normalized * Time.deltaTime * speed);
返回 magnitude 为 1 时的该向量。(只读)
进行标准化时,向量方向保持不变,但其长度为 1.0。
Vector3.Normalize (Vector3 value)
使该向量的 magnitude 为 1。
进行标准化时,向量方向保持不变,但其长度为 1.0。
transform 和 Transform 的区别
transform 是附加到此 GameObject 的 Transform。
Transform 是一个类,用来描述物体的位置,大小,旋转等等信息。
Translate
Translate 根据 translation 的方向和距离移动变换。
塔防项目代码示例
transform.Translate(Vector3.forward * speed * Time.deltaTime);
public void Translate(Vector3 translation);
public void Translate(Vector3 translation, Space relativeTo);
如果 relativeTo 被省略或设置为 Space.Self,则会相对于变换的本地轴来应用该移动。(在场景视图中选择对象时显示的 X、Y 和 Z 轴。) 如果 relativeTo 为 Space.World,则相对于世界坐标系应用该移动。
MonoBehaviour相关
什么时候不继承MonoBehaviour类?
如果你的类不需要引擎提供的初始化、各种物理、渲染或着色器的回调,最好不要继承 MonoBehaviour,因为游戏是需要优化节约资源的。继承后,当引擎接触事件,触发的时候,就会反射调用个各种函数,这是很需要耗费资源的。
不继承会发生什么?
- 不能使用 Invoke 和 Coroutine 了。
- 调试不方便了,不能在 Inspector 和 Debug tab 看到参数。同时如果一个类不继承 Mono,那么这个类的 List 表也无法在 Debug 界面看到。
- 不自动调用 Start,Update 等方法。
谁要继承?
GameObject 还是要继承 MonoBehaviour 的,这样才能 new 实例化一个对象,MonoBehaviour 里面的几十方法比如:
Start(), Awake(), Update(), FixedUpdate(), OnGUI(),这几个方法不管你用哪一个,必须要继承MonoBehaviour
如果有些内容不需要继承,但是有些参数需要显示在Inspector,要怎么做?
[System.Serializable]
可以在检视面板中嵌入带有子属性的类。这种类不需要继承MonoBehaviour,继承MonoBehaviour会浪费计算资源,遇到不需要继承MonoBehaviour,但是又想让这个类的子属性可以在检视面板中检视,就可以用这个
塔防项目代码示例
[System.Serializable]
public class Wave
{
public GameObject enemyPrefab;
public int count;
public float rate;
}
额外参考:Unity3d学习日记:使用[System.Serializable]
[HideInInspector]
使变量不显示在 Inspector 中,但进行序列化。
GetComponent()
从当前游戏对象获取组件T,只在当前游戏对象中获取,没得到的就返回null,不会去子物体中去寻找。
塔防项目代码示例
MapCube mapCube = hit.collider.GetComponent<MapCube>()
col.GetComponent<Enemy>().TakeDamage(damage);
//upgradeCanvas在Inspector界面挂载了Animator组件,所以通过GetComponent来把这个组件赋值给同为Animator 类的upgradeCanvasAnimator
public GameObject upgradeCanvas;
private Animator upgradeCanvasAnimator;
upgradeCanvasAnimator = upgradeCanvas.GetComponent<Animator>();
Quaternion
Quaternion.identity
单位四元数,也就是默认的无旋转状态,此时与世界坐标相同,前方指向+Z,上方指向+Y
塔防项目代码示例
for(int i = 0; i<wave.count; i++)
{
GameObject.Instantiate(wave.enemyPrefab, START.position,Quaternion.identity);
CountEnemyAlive++;
if (i != wave.count-1)
yield return new WaitForSeconds(wave.rate);
}
Quaternion(四元数)用于计算Unity旋转。
额外学习: 【Unity技巧】四元数(Quaternion)和旋转
GameObject.Instantiate
Ray相关
塔防项目代码示例
//炮台建造
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
RaycastHit hit;
bool isCollider = Physics.Raycast(ray, out hit, 1000, LayerMask.GetMask("MapCube"));
if (isCollider)
{
MapCube mapCube = hit.collider.GetComponent<MapCube>();//得到点击的MapCube
if (selectedTurretData != null && mapCube.turretGo == null)
{
//如果得到的mapCube是空的,代表可以创建炮台
if (money > selectedTurretData.cost)
{
ChangeMoney(-selectedTurretData.cost);
mapCube.BuildTurret(selectedTurretData);
}
else
{
//钱不够的提示
moneyAnimator.SetTrigger("Flicker");
}
}
else if(mapCube.turretGo != null)
{
//升级炮台
if(mapCube.turretGo == selectedMapCube && upgradeCanvas.activeInHierarchy)
{
StartCoroutine(HideUpgradeUI());
}
else
{
ShowUpgradeUI(mapCube.transform.position, mapCube.isUpgraded);
}
selectedMapCube = mapCube;
}
}
Camera.ScreenPointToRay
官方文档
虽然是Camera类的函数,但是 ScreenPointToRay 返回 Ray 类的值
Physics.Raycast
官方文档
如果射线与任何碰撞体相交,返回 true,否则为 false。
Extra:out hit
Unity3D教程:ref 、out、params参数区别
RaycastHit hit
官方文档
Input.GetMouseButtonDown()
button 值为 0 表示主按钮(通常为左键),1 表示右键,2 表示中间按钮。
关于 Text 和 text
塔防项目代码示例
public Text moneyText;
moneyText.text
Text: 类
text: 使用此设置可读取或编辑 Text 中显示的消息。使用其他 Text 属性,如 size、font 和 alignment,可更改 text 的外观。
Instantiate
塔防项目代码示例
GameObject bullet = GameObject.Instantiate(bulletPrefab, firePosition.position, firePosition.rotation);
OnTriggerEnter(Collider)
OnTriggerStay(Collider)
OnTriggerExit(Collider)
顺便附上一个触发器和碰撞器的介绍吧:Unity 触发器与碰撞器介绍,不要弄混!
List
(仅介绍实例用法)
塔防项目代码示例
public **List<GameObject> enemies** = new List<GameObject>();
List<int> emptyIndex = new List<int>();
transform.LookAt()
塔防项目代码示例
transform.LookAt(target.position);
head.LookAt(targetPosition); //head 已经是 Transform 类,所以可以直接调用 LookAt()
laserEffect.transform.LookAt(pos); //laserEffect 是 GameObject 类,所以先调用 transform类再调用LookAt()
MonoBehaviour.OnMouseEnter()
当鼠标进入 GUIElement 或 Collider 时调用。
当鼠标停留在对象上时,调用相应的 onm ouseOver 函数; 当鼠标移开时,调用 onm ouseExit。
官方文档
MonoBehaviour.OnMouseExit()
当鼠标不再处于 GUIElement 或 Collider 上方时调用。
onm ouseExit 调用跟随在相应的 onm ouseEnter 和 onm ouseOver 调用之后。
在属于 Ignore Raycast 层的对象上,不调用该函数。
官方文档
EventSystem.current.IsPointerOverGameObject()
EventSystem
创建了UI之后就会出现。在这个塔防游戏中,EventSystem监视游戏里的UI界面(红框内):
IsPointerOverGameObject()
具有给定 ID 的指针是否位于 EventSystem 对象上?(如果使用没有参数的 IsPointerOverGameObject(),它将指向“鼠标左键”(pointerId = -1);
官方文档
塔防项目代码示例
//当鼠标进入collider的时候,如果方块上没有炮塔,且鼠标没有在UI上(即IsPointerOverGameObject() == false),执行方块变色的动作
private void onm ouseEnter()
{
if(turretGo == null && EventSystem.current.IsPointerOverGameObject() == false)
{
renderer.material.color = Color.red;
}
}
Slider
UnityEngine.UI里的类
塔防项目代码示例
public Slider hpSlider;
hpSlider.value = (float)hp / totalHp;
Button
UnityEngine.UI里的类
interactable
SetActive()
GameObject下的函数
激活或停用对象:true 可激活 GameObject,而 false 可停用 GameObject。
塔防项目代码示例
//只要炮塔可以升级(isDisableUpgrade = false),那么就把upgradeCanvas激活并把对应的方块位置赋值,并把buttonUpgrade.interactable设置为true,那么游戏里面,升级按钮就可以点击
public Button buttonUpgrade;
public GameObject upgradeCanvas;
void ShowUpgradeUI(Vector3 pos, bool isDisableUpgrade = false)
{
StopCoroutine("HideUpgradeUI");
upgradeCanvas.SetActive(false);
upgradeCanvas.SetActive(true);
upgradeCanvas.transform.position = pos;
buttonUpgrade.interactable = !isDisableUpgrade;
}
GameObject.activeInHierarchy
GameObject下的变量,能返回GameObject是否激活的布尔值
官方文档
塔防项目代码示例
//如果点击方块和当前选择的炮台是一样的,且升级选项都激活了,那么就停止协程,否则就显示升级UI
if(mapCube.turretGo == selectedMapCube && upgradeCanvas.activeInHierarchy)
{
StartCoroutine(HideUpgradeUI());
}
else
{
ShowUpgradeUI(mapCube.transform.position, mapCube.isUpgraded);
}
协程
协程就像一个函数,能够暂停执行并将控制权返还给 Unity,然后在下一帧继续执行。
协程本质上是一个用返回类型 IEnumerator 声明的函数(取代了常用的void声明),并在主体中的某个位置包含 yield return 语句。yield return 行是暂停执行并随后在下一帧恢复的点。
默认情况下,协程将在执行 yield 后的帧上恢复,但也可以使用 WaitForSeconds 来引入时间延迟:
要将协程设置为运行状态,必须使用 StartCoroutine 函数:
有需要的时候,可以使用 StopCoroutine 函数。
塔防项目代码示例
IEnumerator HideUpgradeUI()
{
upgradeCanvasAnimator.SetTrigger("Hide");
yield return new WaitForSeconds(0.11f);
upgradeCanvas.SetActive(false);
}
void Start()
{
StartCoroutine("SpawnEnemy");
}
public void Stop()
{
StopCoroutine("SpawnEnemy");
}
IEnumerator SpawnEnemy()
{
foreach(Wave wave in waves)
{
for(int i = 0; i<wave.count; i++)
{
GameObject.Instantiate(wave.enemyPrefab, START.position, START.rotation);
CountEnemyAlive++;
if (i != wave.count-1)
yield return new WaitForSeconds(wave.rate);
}
while (CountEnemyAlive>0)
{
yield return 0;
}
yield return new WaitForSeconds(waveRate);
}
while (CountEnemyAlive > 0)
{
yield return 0;
}
GameManager.Instance.Win();
}
游戏LoadScene以及结束游戏
塔防项目代码示例
using UnityEngine.SceneManagement;
public class GameMenu : MonoBehaviour
{
public void OnStartGame()
{
SceneManager.LoadScene(1);
}
public void OnExitGame()
{
//宏,如果在unity编辑器里面运行游戏,点击退出的时候,就设置UnityEditor.EditorApplication.isPlaying为false,这样就能退回到编辑模式。
//除此情况外,就调用Application.Quit();来结束游戏
#if UNITY_EDITOR
UnityEditor.EditorApplication.isPlaying = false;
#else
Application.Quit();
#endif
}
}