Unity笔记——简单人物与机关控制器的实现

在Unity脚本组件中被定义为public类型的变量,能在物体的属性窗口中显示出来,基于这个特性,我们可以很方便地为游戏中的“某一类”物体写一个控制器,用于控制一些逻辑上相似的行为。因此,如何尽可能提高书写的代码的灵活性、复用性,提高劳动效率也成为了Unity的一个重要课题,接下来,我们就在这个思想的基础上用Unity 2D完成两个简单的控制器。

我提前准备了一些最基本的几个Sprite,用它们可以模拟大部分的游戏场景,其中“Error”就是我们的Player,为了方便起见,我给它起名为艾若,今后大部分的模拟都由它来完成。那么话不多说,就先让我们用这些基本部件将我们的场景先搭起来。

现在,可以看见我已经用需要Sprites将基本的场景搭建起来了,并给了艾若刚体(附加物理属性)和胶囊碰撞器,给了地面和方块盒型碰撞器,但是因为我们还没艾若添加任何控制运动的组件,所以无法进行任何移动。接下来,就让我们一起来完成吧
注意:因为这是个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中的代码,之前写的代码我不会写第二次
因为我们射线检测的是Ground层,所以首先新建立一个”Ground”层级并让需要检测的Prites改为此层级,然后测试结果,能看到我们用于地面检测的线段,跳跃、转向等功能也已经完美实现,一个简单又好用的人物运动控制器就此完成

现在,再让我们顺势完成一个机关控制器吧


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

可以看到,我在这里重新对场景进行了搭键,放了一个触发器(小圆点)和一个响应器(桥),现在我们要用我们写的控制器让小圆点操控桥的升起和降落

先完成触发器部分的代码

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);
    }
    //将对应的函数写好即可,若开关开启,则桥倒下,否则桥保持直立
}
行了,主要脚本都完成,现在我们只需要将TriggerController和BridgeController分别给予相应的物体即可,当然不要忘了将TriggerController中对应的Appliiance进行绑定,以此来实现一对一的控制效果
行了,让咱们来看看最终效果吧

当然,我们的机关可不仅仅只能控制桥哦。只要是有两个状态控制的机关,其实都可以用这套控制器来实现,只要为开关所对应的装置分别写一个IsOn和IsOff函数就可以了,而再这两个函数当中想要实现什么样具体的功能,都可以由你自己来决定哦。


今天的笔记就到这里,其实也都是一些十分基础简单的东西,希望对大家能有所帮助,咱们下回再见。

发布者

大瓜子

大瓜子

bug与代码齐飞,头顶同皮肤一色。

发表评论