https://learn.unity.com/?_gl=1*1c5ya13*_ga*MTAzODc3MzI4LjE1OTc5MTQ3NzI.*_ga_1S78EFL1W5*MTYyNjM0NTY2MC4xNy4xLjE2MjYzNTM5ODAuNQ..&_ga=2.119106061.528537968.1626154667-103877328.1597914772
1. void OnTriggerEnter2D(Collider2D other){} //2d游戏与触发器开始重叠,other内包含与当前触发器碰撞的对象的信息
RubyController controller = other.GetComponent
添加尖括号的原因是 GetComponent 方法为泛型。泛型方法具有两组不同的参数:普通参数和类型参数。尖括号之间列出的参数是类型参数。
2. 脚本中的public的变量将在unity脚本中显示出来,可进行重载;
3. public int health { get { return currentHealth; } } //让外部获取变量但不可修改的只读函数示例。
4.
Rigidbody2D rigidbody2d;
rigidbody2d = GetComponent
horizontal = Input.GetAxis("Horizontal");
vertical = Input.GetAxis("Vertical"); //获得坐标
刚体运动
Vector2 position = rigidbody2d.position;
position.x = position.x + speed * horizontal * Time.deltaTime;
position.y = position.y + speed * vertical * Time.deltaTime;
动作表现
animator.SetFloat("Move X", 0); animator.SetFloat("Move Y", direction);
在此部分中,如果机器人垂直移动,则会将 0 发送到 horizontal 参数,而 direction 定义机器人是向上还是向下移动。
5.
rigidbody2d.MovePosition(position); //刚体移动
6.
currentHealth = Mathf.Clamp(currentHealth + amount, 0, maxHealth);
//使结果不能超过前后两个数值。
7.
void FixedUpdate() //非每帧更新,每隔一个固定时间更新一次
8.
void OnCollisionEnter2D(Collision2D other) //刚体与某个对象碰撞时调用的函数
9.
Mathf.Approximately(move.x, 0.0f) //类似==,因为计算机存储浮点数的方式意味着精度会有很小的损失。所以,不应测试完美的相等性,因为最终结果应该是 0.0f 的运算可能会得出类似 0.0000000001f 的结果。Approximately 会考虑这种不精确性,如果除去这种不精确性可以认为数值相等,将返回 true。
.Normalize() //使长度等于1,用在vector2里作为方向储存
.magnitude //计算向量长度
10.
GameObject projectileObject = Instantiate(projectilePrefab, rigidbody2d.position + Vector2.up * 0.5f, Quaternion.identity);
//Instantiate 是 Unity 的一个函数,Instantiate 的第一个参数是一个对象,在第二个参数的位置处创建一个副本,第三个参数是旋转。
四元数 (Quaternion) 是一种可以表达旋转的数学运算符,但这里只需要记住的是,Quaternion.identity 表示“无旋转”。
11.
if(transform.position.magmitude >1000.0f) //position 可以看作是从世界中心到对象所在位置的向量,magnitude 是该向量的长度。因此,position 的 magnitude 就是到中心的距离。
可以获取角色和齿轮之间的距离(使用 Vector3.Distance (a,b) 函数计算 position a 与 position b 之间的距离)。
12.
smokeEffect.Stop(); //
注释掉 smokeEffect.Stop() 并在下一行添加 Destroy(smokeEffect.gameObject)。
运行场景,并修复机器人。
烟雾会立即消失。这是因为当粒子系统被销毁时,也会销毁当前正在处理的所有粒子,即使是刚刚创建的粒子。
但 Stop 只会阻止粒子系统创建粒子,已经存在的粒子可以正常结束自己的生命周期。这比所有粒子突然消失要看起来自然得多。
13.
实例化粒子系统
与飞弹一样,从粒子系统制作预制件后,便可以使用 Instantiate 函数创建粒子系统。
因此,对于仅发生一次的效果(例如被击中或拾取生命值可收集对象),你可以将对预制件的引用存储在公共变量中,并在应该产生效果时调用 Instantiate。如果 Particle System 未设置为 Looping 并且 Stop Action 设置为 Destroy,则会播放效果,然后销毁。
14。
originalSize = mask.rectTransform.rect.width; //获取UI的宽度
mask.rectTransform.SetSizeWithCurrentAnchors(RcctTransform.Axis.Horizontal, originalSize * value); //以当前锚点设置大小
代码行 using UnityEngine; 让你无需在 UnityEngine 中的所有类(如 GameObject)前面键入 UnityEngine。所以,如果你在 using.UnityEngine; 代码行下面直接添加 using UnityEngine.UI; 代码行,你的代码将会正确编译,因为现在可以在该脚本内直接使用 Image 了。
15.
public class UIHealthBar : MonoBehaviour {
public static UIHealthBar instance { get; private set; }
void Awake() { instance = this; }
static UIHealthBar instance 属性表示是静态的公共属性,因此可以在任何脚本中编写 UIHealthBar.instance,都会调用该 get 属性。set 属性是私有属性,因为我们不希望其他人能够从脚本外部进行更改。
然后在你的 Awake 函数中(注意,一旦创建了对象,在我们这种情况下就是指游戏开始后,便会调用此函数),你在静态实例中存储了 this,这是一个特殊的 C# 关键字,表示“当前运行该函数的对象”。
16.
if (Input.GetKeyDown(KeyCode.X))
{ RaycastHit2D hit = Physics2D.Raycast(rigidbody2d.position + Vector2.up * 0.2f, lookDirection, 1.5f, LayerMask.GetMask("NPC"));
首先,你将声明一个类型为 RaycastHit2D 的新变量。此变量存储由 Physics2D.Raycast 提供的 Raycast 的结果。
Physics2D.Raycast 有多种版本,但我们在这里使用的版本有四个参数:
你的示例中的起点是从 Ruby 的位置向上偏移,因为你想从 Ruby 精灵中心进行测试,而不是从她的双脚。
方向,这是 Ruby 注视的方向。
射线的最大距离应设置为 1.5,这样射线投射不会测试相距起点 1.5 个单位的相交点。
一个图层遮罩,允许我们仅测试某些图层。在相交测试期间,所有不属于遮罩的图层都将被忽略。在这里,你将选择 NPC 图层,因为该图层包含你的青蛙。
17.
名称开头的 m_ 是什么意思?这是命名约定的一部分。命名约定用于标识特定对象或对象类。在此项目中,您将使用 Unity 的内部命名约定。所有变量均以小写字母开头,但随后的单词以大写字母开头(这称为 camelCase)
例外情况是非公共成员变量,这种变量以 m_ 前缀开头,所有单词均以大写字母开头(这称为 PascalCase)。成员变量是属于类而不是属于特定方法的变量。非公共成员变量的 m_ 部分起源于它们是“成员 (member)”变量。
18.
m_Movement.Set(horizontal, 0f, vertical);
3D 空间中的矢量具有三个值 — 此 Set 方法为每一个分配一个值。该方法有三个参数,矢量的每个坐标对应一个参数。
移动矢量由两个数字组成,这两个数字的最大值可以为 1。如果它们两者的值都为 1,则矢量的长度(称为其大小)将大于 1。这便是勾股定理描述的三角形的边之间的关系。
这意味着角色沿对角线移动的速度将比沿单个轴的移动速度更快。为了确保不会发生这种情况,您需要确保移动矢量始终具有相同的大小。为此,可对其进行标准化。对矢量进行标准化意味着保持矢量的方向相同,但是将其大小更改为 1。
m_Movement.Normalize ();
19.
Vector3 desiredForward = Vector3.RotateTowards (transform.forward, m_Movement, turnSpeed * Time.deltaTime, 0f);
此代码创建一个名为 desiredForward 的 Vector3 变量。
该变量设置为名为 RotateTowards 的方法的返回值,这个方法是 Vector3 类中的一个静态方法。RotateTowards 接受四个参数:前两个是 Vector3,分别是旋转时背离和朝向的矢量。
该代码以 transform.forward 开头,目标是 m_Movement 变量。transform.forward 是访问 Transform 组件并获取其前向矢量的快捷方式。
接下来的两个参数是起始矢量和目标矢量之间的变化量:首先是角度变化(以弧度为单位),然后是大小变化。此代码中的角度变化为 turnSpeed * Time.deltaTime,而大小变化为 0。
接下来,使用矢量来获取并存储旋转,以便可以在任何需要的地方使用。存储移动矢量一样存储旋转,因此在该位置声明其变量很合理。
Quaternion m_Rotation = Quaternion.identity;
四元数 (Quaternion) 是存储旋转的一种方式,可用于解决将旋转存储为 3D 矢量时遇到的一些问题。本教程不会详细探讨四元数,现在只需要知道它们是一种存储旋转的方式即可。
为 Quaternion 指定默认值 Quaternion.identity。通常情况下,创建类的实例时,会将属于类的变量(成员变量)而不是属于特定方法的变量设置为默认值。例如,Vector3 的默认值是将 x、y 和 z 都设置为 0。四元数也是如此。但是,虽然零矢量是合理的(因为没有移动),零四元数却不太合理。这里设置的 Quaternion.identity 值就是为其赋予无旋转的值,这是一个更合理的默认值。
创建了旋转变量,现在可以对其进行设置。在创建 desiredForward 变量的代码行下面,添加以下行:
m_Rotation = Quaternion.LookRotation (desiredForward);
该行代码仅调用 LookRotation 方法,并在给定参数的方向上创建旋转。
20.
该角色有一段有趣的 Walk 动画,最好为此使用根运动。但是,该动画中没有任何旋转,如果尝试在 Update 方法中旋转刚体 (Rigidbody),则动画可能会覆盖该刚体(这可能导致角色在应该旋转的时候不旋转)。
实际需要的是动画的一部分根运动,但不是全部的根运动;具体来说,需要应用移动而不是旋转。那么如何更改从 Animator 中应用根运动的方式呢?幸运的是,MonoBehaviour 有一种特殊的方法可用于更改从 Animator 中应用根运动的方式。
在 Update 方法下面,声明一个新方法:
void OnAnimatorMove () { }
让我们先从移动开始吧。将以下行添加到新的 OnAnimatorMove 方法中:
m_Rigidbody.MovePosition (m_Rigidbody.position + m_Movement * m_Animator.deltaPosition.magnitude);
首先,要使用对 Rigidbody 组件的引用来调用其 MovePosition 方法,并传入唯一的参数:其新位置。新位置从刚体的当前位置开始,然后您在此基础上添加一个更改 — 移动矢量乘以 Animator 的 deltaPosition 的大小。但是,这是什么意思?
Animator 的 deltaPosition 是由于可以应用于此帧的根运动而导致的位置变化。将其大小(即长度)乘以我们希望角色移动的实际方向上的移动向量。
21.
以前,查找对组件的引用时,使用的是 GetComponent 方法,但这仅适用于与此脚本在同一游戏对象上的组件。 为 Canvas Group 组件创建一个可以在 Inspector 中分配的公共变量。 在 player 变量声明下面添加以下公共变量声明:
public CanvasGroup exitBackgroundImageCanvasGroup;
22.
在代码块中添加以下代码行以退出游戏:
Application.Quit();
这里有一些重要的知识需要了解。该方法确实可以退出游戏,但仅适用于完全构建的应用程序。目前,该游戏只是在 Editor 中播放的一个项目,因此尚无任何作用。
23.
使用 Observer 类来检测玩家的角色。在脚本中的花括号之间,添加以下行:
public Transform player;
if(other.transform == player)
该脚本将检查玩家角色的 Transform,而不是其游戏对象。这样可以更轻松地了解 JohnLemon 的位置并确定是否可以清楚看到他。
24.
有了方向,接下来便可以创建射线 (Ray)。在 direction 变量下面添加以下代码行:
Ray ray = new Ray (transform.position, direction);
Unity 中有很多不同的 Raycast 方法,但是所有这些方法都有两个共同点:它们需要定义发生射线投射的射线,以及关于要检测的碰撞体类型的限制。 您将使用的 Raycast 方法会返回一个布尔值,当射线投射击中某个对象时为 true,否则为 false。因为返回的是布尔值,所以将 Raycast 方法放在 if 语句中非常方便。if 语句的代码块仅在射线投射击中某个对象时才执行。 在创建射线 (Ray) 的位置下方,并且仍在之前编写的 if 语句内,添加以下代码:
if(Physics.Raycast(ray)) { }
已经定义了射线,并且没有限制射线投射可以检测到的碰撞体。对碰撞体的限制往往用作过滤器,而不是识别单个碰撞体。需要的是有关射线投射 (Raycast) 准确击中的对象的信息,以便可以检查这是否是玩家角色。 但有一个问题:从方法中获取信息的方式是通过其返回值,而这里的 Raycast 仅返回布尔值。但幸运的是有非常相似的 Raycast 方法,这个方法使用 out 参数。 out 参数的工作方式与普通参数相同,不同之处是参数前面要写关键字“out”。这些参数的值可以由它们的方法更改或设置,以便能够由任何调用方使用。out 参数有一个名为 RaycastHit 的类型,并且 Raycast 方法会将其数据设置为有关射线投射命中的任何对象的信息。 在创建 Ray 的代码行与 Raycast if 语句之间,添加以下代码:
RaycastHit raycastHit;
该行代码定义了 RaycastHit 变量。
接下来,需要更改 Raycast 方法以便使用此变量。将包含 Raycast 方法的 if 语句更改为如下所示:
if(Physics.Raycast(ray, out raycastHit)) { }
这是一个不同的 Raycast 方法,其工作方式非常类似,但使用 out 参数返回信息。
现在,脚本可以识别玩家的角色是否在范围内、执行射线投射并知道是否有任何对象被击中。接下来,需要检查击中了什么。
在 Raycast 的 if 语句中,添加以下代码:
if(raycastHit.collider.transform == player) { }
25.
为了给 Nav Mesh Agent 编写任何脚本,需要包含其命名空间。 在脚本顶部与其他 using 指令一起,添加以下代码:
using UnityEngine.AI;
包含 AI 命名空间将让您能够访问 NavMeshAgent 类。
public NavMeshAgent navMeshAgent;
这个对组件的公共引用将使您能够在 Inspector 窗口中分配 Nav Mesh Agent 引用。
需要设置幽灵应该巡逻的路径点。Nav Mesh Agent 的目标是 Vector3 矢量,即世界空间中的位置。但是,假如将路径点设置为 Vector3,则必须手动设置所有位置,并希望使用的数字准确无误。 相反,合理的做法是采用一组空游戏对象并将它们的位置用作路径点。但是,可以不引用一组游戏对象,而只是引用这些游戏对象的 Transform 组件。可以直接声明几个公共 Transform 变量,然后在 Inspector 窗口中分别设置每个变量,但在每个幽灵可以拥有的路径点数量方面,这种方案没有太大的灵活性。 实际上,更好的方案是使用数组。数组是一起存在的变量的基本集合。数组是通过方括号定义的。 在 navMeshAgent 变量声明下面添加以下行
public Transform[] waypoints;
让我们继续向脚本添加:
.当幽灵到达设置为目标的最后一个路径点时,需要继续前进到下一个路径点。跟踪下一个路径点的最简单方法是存储路径点数组的当前索引。 在 waypoints 数组声明下面,添加以下代码:
int m_CurrentWaypointIndex;
.接下来,让我们前往 Update 方法,以便可以使用这个索引。 在 Update 方法中,需要执行检查:您想知道 Nav Mesh Agent 是否已到达其目标。一种简单的检查方法是查看到目标的剩余距离是否小于先前在 Inspector 窗口中设置的停止距离。 在 Update 方法中添加以下 if 语句:
if(navMeshAgent.remainingDistance < navMeshAgent.stoppingDistance) { }
.现在,需要更新当前索引,然后将其用于设置 Nav Mesh Agent 的目标。为此,将使用一个称为求余运算符的新运算符,该运算符由百分比字符 % 表示。 在 if 语句中添加以下代码:
m_CurrentWaypointIndex = (m_CurrentWaypointIndex + 1) % waypoints.Length;
代码表示:“将当前索引加 1,但是如果该增量导致索引等于 waypoints 数组中元素的数量,则将索引设置为零。”在这种情况下,索引将被设置为零,因为任何数字除以本身时,余数均为零。
现在已经增加了索引(并在必要时将其循环回零),接下来需要使用索引。在索引增量设置下面添加以下代码:
navMeshAgent.SetDestination (waypoints[m_CurrentWaypointIndex].position);
这与 Start 方法中所做的完全一样,唯一不同之处是使用幽灵当前所在的路径点作为索引,而不是使用零作为索引。
26.
if(!m_AudioSource.isPlaying) { }
向 if 语句中添加对 Play 方法的调用:
m_AudioSource.Play ();
要停止播放音频源,请将以下代码添加到 else 语句中:
m_AudioSource.Stop ();
27.
if (Input.GetKeyDown(KeyCode.R)) { GetComponent
28.全屏地图
public static FullscreenMap Instance { get; private set; }
public RawImage MapImageTarget;
public RectTransform Arrow;
public int Resolution;
public MinimapSystem.MinimapSystemSetting MinimapSystemSettings;
public RenderTexture RenderTexture => m_RT;
RenderTexture m_RT;
float m_Ratio;
void Awake()
{
Instance = this;
gameObject.SetActive(false);
m_Ratio = MapImageTarget.rectTransform.rect.height / MapImageTarget.rectTransform.rect.width;
m_RT = new RenderTexture(Resolution, Mathf.FloorToInt(Resolution * m_Ratio), 16, RenderTextureFormat.ARGB32);
MapImageTarget.texture = m_RT;
}
public void UpdateForPlayerTransform(Transform playerTransform)
{
MinimapSystem.Render(m_RT, playerTransform.position, playerTransform.forward, MinimapSystemSettings);
if (MinimapSystemSettings.isFixed)
{
Arrow.rotation = Quaternion.Euler(0, 0, Vector3.SignedAngle(playerTransform.forward, Vector3.forward, Vector3.up));
}
else
{
Arrow.rotation = Quaternion.identity;
}
}
29. 迷你地图
public class MinimapSystem
{
[System.Serializable]
public struct MinimapSystemSetting
{
public float heightStep;
public float halfSize;
public float wallThickness;
public Material material;
public bool isFixed;
}
static int s_WallThicknessId;
static MinimapSystem()
{
s_WallThicknessId = Shader.PropertyToID("_WallThickness");
}
public static void Render(RenderTexture renderTarget, Vector3 origin, Vector3 forward, MinimapSystemSetting settings)
{
settings.material.SetFloat(s_WallThicknessId, settings.wallThickness);
float aspectRatio = renderTarget.width / (float)renderTarget.height;
CommandBuffer buffer = new CommandBuffer();
Matrix4x4 lookAt;
Vector3 camPos = origin + Vector3.up * 3.0f;
if (settings.isFixed)
{
lookAt = Matrix4x4.TRS(camPos, Quaternion.LookRotation(Vector3.down, Vector3.forward), new Vector3(1, 1, -1)).inverse;
}
else
{
lookAt = Matrix4x4.TRS(camPos, Quaternion.LookRotation(Vector3.down, forward), new Vector3(1, 1, -1)).inverse;
}
buffer.SetRenderTarget(renderTarget);
buffer.SetProjectionMatrix(Matrix4x4.Ortho(-settings.halfSize * aspectRatio, settings.halfSize * aspectRatio, -settings.halfSize , settings.halfSize , 0.5f, 1.5f));
buffer.SetViewMatrix(lookAt);
buffer.ClearRenderTarget(true,true, Color.black);
foreach (var r in MinimapElement.Renderers)
{
buffer.DrawRenderer(r, settings.material);
}
Graphics.ExecuteCommandBuffer(buffer);
}
}
public class MinimapElement : MonoBehaviour
{
public static List
static List
Renderer m_Renderer;
void OnEnable()
{
m_Renderer = GetComponent
if(m_Renderer != null)
s_Renderers.Add(m_Renderer);
}
void OnDisable()
{
if (m_Renderer)
s_Renderers.Remove(m_Renderer);
}
}
public class MinimapUI : MonoBehaviour
{
public static MinimapUI Instance { get; private set; }
public RawImage MapImageTarget;
public RectTransform Arrow;
public int Resolution;
public MinimapSystem.MinimapSystemSetting MinimapSystemSettings;
public RenderTexture RenderTexture => m_RT;
RenderTexture m_RT;
float m_Ratio;
bool m_HeightWasInited;
float m_InitialHeight;
void Awake()
{
Instance = this;
}
void Start()
{
m_HeightWasInited = false;
m_Ratio = MapImageTarget.rectTransform.rect.height / MapImageTarget.rectTransform.rect.width;
m_RT = new RenderTexture(Resolution, Mathf.FloorToInt(Resolution * m_Ratio), 16, RenderTextureFormat.ARGB32);
MapImageTarget.texture = m_RT;
}
public void UpdateForPlayerTransform(Transform playerTransform)
{
if (!m_HeightWasInited)
{
m_HeightWasInited = true;
m_InitialHeight = playerTransform.position.y;
}
Vector3 usedPosition = playerTransform.position;
float heightDifference = m_InitialHeight - usedPosition.y;
float heightSign = Mathf.Sign(heightDifference);
heightDifference = Mathf.FloorToInt(Mathf.Abs(heightDifference/MinimapSystemSettings.heightStep)) * heightSign * heightDifference;
usedPosition.y = m_InitialHeight + heightDifference;
MinimapSystem.Render(m_RT, usedPosition, playerTransform.forward, MinimapSystemSettings);
if (MinimapSystemSettings.isFixed)
{
Arrow.rotation = Quaternion.Euler(0, 0, Vector3.SignedAngle(playerTransform.forward, Vector3.forward, Vector3.up));
}
else
{
Arrow.rotation = Quaternion.identity;
}
}
}