Unity 2D 游戏学习笔记(6)

官方教程:世界交互 - 可收集对象 - Unity Learn

        这次我们实现收集对象、伤害区域。

四、收集对象

        做一个吃草莓加血的功能,首先Ruby得有血量,顺便把速度也改成可调整的,注释后加*的代表改动或新的代码。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class RubyControl2 : MonoBehaviour
{
    //总血量*
    public int maxHealth = 5;
    //当前血量*
    int currentHealth;
    //速度*
    public float speed = 3.0f;



    //创建一个刚体变量
    Rigidbody2D rigidbody2d;
    //因为要在不同函数中使用这两个变量,所以要声明在最外侧
    float horizontal;
    float vertical;
    // Start is called before the first frame update
    void Start()
    {
        //将脚本中的Rigidbody2D中的属性赋值给rigidbody2d
        rigidbody2d = GetComponent();

        //开始先满血*
        currentHealth = maxHealth;
    }

    void Update()
    {
        //利用GetAxis函数,调用用户输入的轴的值
        horizontal = Input.GetAxis("Horizontal");
        vertical = Input.GetAxis("Vertical");      
    }
    private void FixedUpdate()
    {
        //得到脚本中Ruby刚体的位置信息
        Vector2 position = rigidbody2d.position;
        //每一帧,x的位置的加速度乘以用户输入移动的量*
        position.x += speed * horizontal * Time.deltaTime;
        position.y += speed * vertical * Time.deltaTime;
        //将移动的距离返回给Ruby刚体
        rigidbody2d.MovePosition(position);
    }

    //生命值变化*
    void ChangeHealth(int amount)
    {
        //使用函数Mathf中的方法clamp,让第一个参数的值小于第二个参数的值时,等于第二个参数;
        //第一个参数的值大于第三个参数的值时,等于第三个参数。
        currentHealth = Mathf.Clamp(currentHealth + amount, 0, maxHealth);
        //在控制台显示血量
        Debug.Log(currentHealth + "/" + maxHealth);
    }
}

        Ruby的血量有了,接下来需要找到草莓,在Art-->Sprites-->VRX中的CollectibleHealth,将它拖入Hierarchy中,给它添加Box Collider 2D,勾选Is Trigger(触发器),这样就可以记录触碰但是可以穿过草莓。

        我们给草莓编写一个加血脚本,在Scripts文件夹中创建一个新C# Script,命名为HealthCollectible,

        你希望脚本检测 Ruby 与可收集的生命值游戏对象发生碰撞的情况,并向她提供一些生命值。为此,请使用以下特殊函数名称:

void OnTriggerEnter2D(Collider2D other)

        提示:确保函数名称和参数类型的拼写正确,因为 Unity 在需要调用函数时会使用这些名称在脚本中“查找”函数。

        与 Unity 每帧调用 Update 函数的方式相同,当检测到新的刚体进入触发器时,Unity 将在第一帧调用此 OnTriggerEnter2D 函数。名为 other 的参数将包含刚进入触发器的碰撞体。记得要将RubyController中的ChangeHealth前加上public。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;


public class HealthCollectible : MonoBehaviour
{
    private void OnTriggerEnter2D(Collider2D other)
    {
        //访问进入触发器的碰撞体的游戏对象上的RubyController组件
        RubyControl2 controller = other.GetComponent();
        //因为触发条件是有物体碰触,所以当不是Ruby碰触时,Controller为空
        if(controller != null)
        {
            //加1血
            controller.ChangeHealth(1);
            //吃完草莓消失
            Destroy(gameObject);
        }
    }
}

        将血量改成1血,运行游戏吃草莓发现控制台返回2/5,并且草莓消失。

        但是当Ruby满血时,不让它吃草莓防止浪费。实现很简单只需要这样

在RubyControl中:

public int health { get { return currentHealth; } }
    //当前血量
    int currentHealth;

在HealthCollectible中: 

if(controller != null)
        {
            if(controller.health < controller.maxHealth)
            {
                //加1血
                controller.ChangeHealth(1);
                //吃完草莓消失
                Destroy(gameObject);
            }
            
        }

        为什么要这么写?因为游戏很多属性不适合用public公开,这样可能与其他脚本里重复或者很容易被其他人更改,所以我们以只读的方式在RubyControl中重新定义了个health,这样它既无法被更改,又可以被其他脚本读取到。

        现在运行游戏,会发现满血时触碰草莓就没反应了。

五、伤害区域

        这次做一个进到刺里持续掉血的功能。首先找到游戏里的Assets > Art > Sprites > Environment 中为区域找到一个名为 Damageable 的精灵,将其拖进Hierarchy窗口中,给它添加Box Collider 2D,勾选Is Trigger。

        然后我们在Scripts文件夹中创建新C# Script编写它的脚本。

public class DamageZone : MonoBehaviour
{
    //要注意一定是2D
    private void OnTriggerEnter2D(Collider2D other)
    {
        //获取到控制Ruby的组件
        RubyControl2 Controller = other.GetComponent();
        //如果Ruby进入到伤害范围内
        if(Controller != null)
        {
            //让它血-1
            Controller.ChangeHealth(-1);
        }
    }
}

        运行后就可以实现进入掉血了,但是没有持续掉血效果。通过设定无敌时间来实现该效果。

        可以通过将函数名称从 OnTriggerEnter2D 更改为 OnTriggerStay2D 来解决此问题。刚体在触发器内的每一帧都会调用此函数,而不是在刚体刚进入时仅调用一次。

        但是按照帧率减血,一下就死了,而且你可能还注意到,当你停止移动 Ruby 时,你在控制台上不会收到任何消息,因此 Ruby 站着不动时不会受到伤害。

        要解决最后这个问题,你需要打开角色预制件,然后在 Rigidbody2D 组件中将 Sleeping Mode 设置为 Never Sleep

Unity 2D 游戏学习笔记(6)_第1张图片

         解决掉血过快,让Ruby有两秒的无敌时间就行了。代码和注释如下,更改代码用*标注。

public class RubyControl2 : MonoBehaviour
{
    //无敌时间长度*
    public float timeInvincible = 2.0f;
    //判断是否无敌*
    bool isInvincible;*
    //当前消耗的无敌时间(计时器)*
    float invincibleTimer;

    //总血量
    public int maxHealth = 5;
    
    //速度
    public float speed = 3.0f;

    public int health { get { return currentHealth; } }
    //当前血量
    int currentHealth;

    //创建一个刚体变量
    Rigidbody2D rigidbody2d;
    //因为要在不同函数中使用这两个变量,所以要声明在最外侧
    float horizontal;
    float vertical;
    // Start is called before the first frame update
    void Start()
    {
        //将脚本中的Rigidbody2D中的属性赋值给rigidbody2d
        rigidbody2d = GetComponent();

        //开始先满血
        currentHealth = maxHealth;
    }

    void Update()
    {
        //利用GetAxis函数,调用用户输入的轴的值
        horizontal = Input.GetAxis("Horizontal");
        vertical = Input.GetAxis("Vertical");     
        
        //如果是无敌的*
        if(isInvincible)
        {
            //减时间
            invincibleTimer -= Time.deltaTime;
            //当无敌时间消耗光,让是否无敌改为否
            if(invincibleTimer < 0)
            {
                isInvincible = false;
            }
        }
    }
    private void FixedUpdate()
    {
        //得到脚本中Ruby刚体的位置信息
        Vector2 position = rigidbody2d.position;
        //每一帧,x的位置的加速度乘以用户输入移动的量
        position.x += speed * horizontal * Time.deltaTime;
        position.y += speed * vertical * Time.deltaTime;
        //将移动的距离返回给Ruby刚体
        rigidbody2d.MovePosition(position);
    }

    public void ChangeHealth(int amount)
    {
        //如果是减血,则判断是否无敌时间*
        if(amount < 0)
        {
            //如果是无敌,就不减血了,返回
            if (isInvincible)
                return;
            //如果不是无敌时间,把状态改为无敌,
            isInvincible = true;
            //添加无敌时间
            invincibleTimer = timeInvincible;
        }
        //使用函数Mathf中的方法clamp,让第一个参数的值小于第二个参数的值时,等于第二个参数;
        //第一个参数的值大于第三个参数的值时,等于第三个参数。
        currentHealth = Mathf.Clamp(currentHealth + amount, 0, maxHealth);
        //在控制台显示血量
        Debug.Log(currentHealth + "/" + maxHealth);
    }
}

        更改过后,发现已经实现了无敌时间。

        如果想扩大碰触的面积,直接拉伸图形会导致图形变大变丑失去比例等等,点击Damageable,在他的Inspector窗口中将Draw Mode更改为Tiled,并且将Tile Mode修改为Adaptive。

Unity 2D 游戏学习笔记(6)_第2张图片

         这时会看到有黄色的警告,在project的Environment文件夹中点击Damageable,将Mesh Type更改为Full Rect即可。

Unity 2D 游戏学习笔记(6)_第3张图片

六、敌人

        先在官方教程中下载敌人的图片Bot,存入Ruby图片的那个文件夹。

        敌人跟主角一样,都需要碰撞和移动,所以给它添加Rigidbody2D 和 BoxCollider2D。记得也要给它FreezeRotation  Z,防止它歪了。

        修改一下它的碰撞体在脚底,重力Gravity scale设为0。怪物一般都来回移动,搞个脚本给它,新建C#Script在Scripts文件夹里,命名为EnemyController。

        左右移动的脚本如下:

public class EnemyController : MonoBehaviour
{
    public float speed = 3.0f;
    //用来选择是纵向移动还是横向移动
    public bool vertical;
    //计时器
    public float changeTime = 3.0f;
    //计时器的当前值
    float timer;
    //用来控制方向
    int direction = 1;

    Rigidbody2D robot;
    // Start is called before the first frame update
    void Start()
    {
        //获得机器人的组件对象
        robot = GetComponent();
        //开始时给计时器固定的时间
        timer = changeTime;
    }
    private void Update()
    {
        //计时
        timer -= Time.deltaTime;
        //当计时结束,反向,重新计时
        if(timer < 0)
        {
            direction = -direction;
            timer = changeTime;

        }
    }

    // Update is called once per frame
    void FixedUpdate()
    {
        //机器人刚体的位置信息给position
        Vector2 position = robot.position;
        //如果勾选了vertical,就纵向移动
        if(vertical)
        {
            position.y += Time.deltaTime * speed * direction;
        }
        else
        {
            position.x += Time.deltaTime * speed * direction;
        }
        
        robot.MovePosition(position);
    }   
}

         接下来给敌人增加伤害

private void OnCollisionEnter2D(Collision2D other)
{
      RubyControl2 player = other.gameObject.GetComponent();
      if(player != null)
      {
          player.ChangeHealth(-1);
      }
}

        另一个变化是,没有使用 other.GetComponent,而是使用的 other.gameObject.GetComponent。这是因为此处的 other 类型是 Collision2D,而不是 Collider2D。Collision2D 没有 GetComponent 函数,但是它包含大量有关碰撞的数据,例如与敌人碰撞的游戏对象。因此,在这个游戏对象上调用 GetComponent。

        这样,敌人就设置好了,把它拖进Prefab里变成预制件。

你可能感兴趣的:(Unity学习,unity,游戏,学习,游戏引擎)