在Unity脚本组件中被定义为public类型的变量,能在物体的属性窗口中显示出来,基于这个特性,我们可以很方便地为游戏中的“某一类”物体写一个控制器,用于控制一些逻辑上相似的行为。因此,如何尽可能提高书写的代码的灵活性、复用性,提高劳动效率也成为了Unity的一个重要课题,接下来,我们就在这个思想的基础上用Unity 2D完成两个简单的控制器。
我提前准备了一些最基本的几个Sprite,用它们可以模拟大部分的游戏场景,其中“Error”就是我们的Player,为了方便起见,我给它起名为艾若,今后大部分的模拟都由它来完成。那么话不多说,就先让我们用这些基本部件将我们的场景先搭起来。

注意:因为这是个2D项目,所以相应的刚体和碰撞器组件也要是2D组件
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerController : MonoBehaviour
{
public float moveSpeed; //定义人物的移动速度
public float jumpForce; //定义人物的跳跃力
public float betterJumpForce; //定义奔跑状况下的跳跃力
public bool IsGround; //检测人物是否接触地面
public bool IsJump; //检测人物是否处于跳跃状态
public bool IsRun; //检测人物是否处于移动状态
/*个人建议将各个运动状态都设置一个布尔变量,以此作为各状态之间的开关*/
/*以上变量都设置为公有类型,便于在属性面板中调试*/
private Rigidbody2D rig;
private float savedJumpForce; //用于保存原来的跳跃力
private void Start()
{
rig = this.GetComponent<Rigidbody2D>();
//让rig获取该物体上的Rigidbody2D刚体组件,即我们可以通过rig来改变当前物体的刚体属性
savedJumpForce = jumpForce;
}
private void Update()
{
IsJump = !IsGround; //若物体未接触地面,则说明其处于跳跃状态;相反,若接触地面,则处于未跳跃的状态
if (Mathf.Abs(rig.velocity.x) > 0) IsRun = true;
else IsRun = false;
//当物体速度大于一个值时,则规定其处于奔跑状态
if (IsRun) jumpForce = betterJumpForce;
else jumpForce = savedJumpForce;
//奔跑状态下可以跳的更高
if(Input.GetButtonDown("Jump") && IsJump == false)
{
rig.AddForce(new Vector2(0, jumpForce));
}
//如果按下跳跃键(默认空格,可以在Input Setting中修改)且此时不处于跳跃状态,则给当前物体施加一个向上的力
}
private void FixedUpdate()
{
float h = Input.GetAxis("Horizontal");
/*定义一个浮点型变量h,使用GetAxis方法令其等于"Horizontal"的键值,是从-1到1的一个数,"Horizontal"代表水平方向上的输入,默认为"a","s"键以及左右方向键,
其中,"a"和左方向键值为-1,"s"和右方向键值为1,也可在Input Setting中更改Horizontal的键位;
另外还有一个方法GetAxisRaw,与GetAxis十分相像,但其只能读取-1,0,1三个值,与GetAxis相比少一个平滑渐变的过程 */
this.rig.velocity = new Vector2(h * moveSpeed * Time.fixedDeltaTime, this.rig.velocity.y);
}
/*FiexedUpdate与Update区别在于前者是基于现实中的物理上的时间,每秒钟执行固定的帧数,而Update却是与你的游戏中显卡的渲染程度有关。基于这样的区别,我们通常将
执行的物理方法在FixedUpdate中执行
感兴趣的朋友可以将我们FixedUpdate中的代码放到Update中去,然后建立并在Unity自带的开始菜单中调整不同的渲染模式运行,可以看到明显的区别*/
}

另外,注意将刚体组件中constrains中冻结z轴方向上的选择给勾选上,否则人物在移动过程中会“摔跤


private GameObject Foot; //用于地面检测 public bool IsFacingRight; //检测人物的朝向,我们这里规定朝右为True,朝左为False
private void Start()
{
Foot = GameObject.Find("Foot"); //找到游戏中的Foot物体
IsFacingRight = true; //默认朝向为右
}
private void Update()
{
IsGround = Physics2D.Linecast(this.transform.position, Foot.transform.position, 1 << LayerMask.NameToLayer("Ground"));
//Linecast射线检测,从一个点向另一个点发射一条射线,检测若与规定的层级发生碰撞,则返回为True,否则返回值为False
Debug.DrawLine(this.transform.position, Foot.transform.position, Color.red, 0.5f);
//用DrawLine方法画一条线来观察,从左到右的参数分别表示起始点、终止点、线条颜色、线条显示的延迟时间
}
private void FixedUpdate()
{
if((h > 0 && !IsFacingRight)||(h < 0 && IsFacingRight)) //判断方向是否发生调转
{
IsFacingRight = !IsFacingRight; //朝向改变
this.transform.localScale = new Vector3(-this.transform.localScale.x, this.transform.localScale.y, 0);
//物体Scale中的x与原来相反
}
}
//注意:这仍为PlayerController中的代码,之前写的代码我不会写第二次

现在,再让我们顺势完成一个机关控制器吧
与人物控制器不同,机关控制器包括两部分:触发器和响应器。基本思想是我们对触发器进行操作,让其向响应器发送消息触发机关即可。

先完成触发器部分的代码
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class TriggerController : MonoBehaviour
{
public GameObject Appliiance; //定义共有类型的GameObject用来与装置进行绑定
public bool IsIn; //检测是否在可触发范围内
public bool ButtonOn;
private void Update()
{
if (Input.GetKeyDown(KeyCode.E) && IsIn)
ButtonOn = !ButtonOn;
//如果按下键盘中的E键,ButtonOn与原来相反(一开始为False),以此来表示开和关两种状态
if (ButtonOn)
{
Appliiance.SendMessage("IsOn");
//若ButtonOn为True,则向装置发送消息,触发IsOn函数
this.GetComponent<SpriteRenderer>().color = Color.red;
//若开关触发,则该物体变为红色
}
else
{
Appliiance.SendMessage("IsOff");
//否则触发IsOff函数
this.GetComponent<SpriteRenderer>().color = Color.white;
}
}
private void OnTriggerEnter2D(Collider2D collision)
{
if(collision.CompareTag("Player"))
{
IsIn = true;
}
}
private void OnTriggerExit2D(Collider2D collision)
{
if(collision.CompareTag("Player")) //检测是否为玩家
{
IsIn = false;
}
}
/*关于OnTrigger和OnCollision的用法这里不在赘述,因为OnTrigger方法并非每一帧都会调用,所以这里用了一个布尔类型的IsIn来判断是否在触发器范围内,而
读取键盘输入则在Update中完成*/
}
接下来是桥的部分
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class BridgeController : MonoBehaviour
{
public void IsOn()
{
this.transform.localEulerAngles = new Vector3(0, 0, -90);
}
public void IsOff()
{
this.transform.localEulerAngles = new Vector3(0, 0, 0);
}
//将对应的函数写好即可,若开关开启,则桥倒下,否则桥保持直立
}


当然,我们的机关可不仅仅只能控制桥哦。只要是有两个状态控制的机关,其实都可以用这套控制器来实现,只要为开关所对应的装置分别写一个IsOn和IsOff函数就可以了,而再这两个函数当中想要实现什么样具体的功能,都可以由你自己来决定哦。
今天的笔记就到这里,其实也都是一些十分基础简单的东西,希望对大家能有所帮助,咱们下回再见。