【Unity2D游戏开发入门第二卷】✨Unity入门总结Sunnyland示例(中卷)

在这里插入图片描述
部分功能例如目录跳转,回到顶部功能在这里有问题

追求阅读体验可以转到 ✨本人主战场!✨

✨✨目录










一、入门卷【中卷】

回到顶部

  1. 收集物品(使用动画帧事件)

  2. 相机大小 Size 调节

  3. 简单敌人系统

  4. 简单音效

  5. 简单光照

[入门卷] 7. 收集物品

这一节介绍简单的交互系统,收集物品

使用上卷的知识,我们创建了 cherry 的动画,记得 Pixels Per Uint 设置要统一。这里是 16

我们捡到物品时(触碰),物品会销毁,然后物品会播放特效,所以我们还需要添加一个物品反馈特效

我们新添加一个 C# 脚本文件 Collections.cs

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

public class Collections : MonoBehaviour
{
    
    
    /// <summary>
    /// @brief 反馈结束,销毁自己
    /// </summary>
    public void FeedbackEnd()
    {
    
    
        GameObject.Destroy(gameObject);
    }
}

我们会使用 动画帧事件,这个方法会在物品播放反馈特效的最后一帧调用,销毁自己

接下来更改一些 PlayerController.cs 中的代码,添加这些代码

[Header("物品收集")]
public int m_CollectionsCount = 0;                          // 收集到的物品数量

// --- private ------------------------------------------

/// <summary>
/// @brief 碰撞开关检测
/// </summary>
/// <param name="collision"></param>
private void OnTriggerEnter2D(Collider2D collision)
{
    
    
    if (collision.tag == "Collections")
    {
    
    
        // 收集物品,然后销毁物品对象(脚本在物品身上,用的动画帧事件)
        m_CollectionsCount++;  // 收集物品数目加一

        // 物品播放反馈动画
        Animator collisionAnimator = collision.GetComponent<Animator>();
        collisionAnimator.SetBool("BFeedback", true);
    }
}

做完测试一下,没问题的话,我们可以创建该 物品的 “预制体”,之后就可以快速的创建物品了

A. 小结演示










[入门卷] 8. 相机大小 Size 调节

相机有很多属性可以调节

Size 调节大小,Background 调节背景颜色,还有很多可以试试,这里将相机大小调成了 7

背景颜色是 (51, 51, 120)










[入门卷] 9. 敌人

和 Player 一样,我们用同样的方法创建敌人(这里用熊,是最近新加进来的)

A. 功能目标

让敌人在一定范围内移动,碰到玩家会产生击退的效果,玩家触发受伤动画

B. 功能实现

在创建中创建 Sprite 对象,命名为 bear

(1). 为 bear 添加组件

适当调节碰撞体大小

Sprite Renderer 图层设置为 Foreground,后面不多重复说明图层问题了

(2). 添加脚本

这里我用文件夹整理了脚本文件

1. BearBase.cs 参考

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

public class BearBase : MonoBehaviour
{
    
    
    // --- private -----------------------------------------
    private Rigidbody2D m_Rb;                               // Bear 刚体组件
    //private Animator m_Animator;                          // 动画控制器

    [Header("移动参数")]
    [SerializeField] private Vector2 m_LeftBound;           // 记录左边界向量
    [SerializeField] private Vector2 m_RightBound;          // 记录右边界向量

    [Header("检测参数")]
    [SerializeField] private bool m_BTurnDirection = false; // 判断是否转向 

    // --- public ------------------------------------------
    public float m_HorizontalMovementSpeed = 260.0f;        // 水平移动速度
    public GameObject m_LeftBoundGameObject;                // 左边界
    public GameObject m_RightBoundGameObject;               // 有边界

    [Header("战斗相关")]
    public float m_ForceOfRepelling = 26.0f;                // 击退力(每个敌人的击退力不同)

    private void Start()
    {
    
    
        // 获取组件
        m_Rb = GetComponent<Rigidbody2D>();

        /** 初始化数据 */
        // 保存边界数据(也可以直接赋值,我这里选择 new Vector2)
        m_LeftBound = new Vector2(m_LeftBoundGameObject.transform.position.x, m_LeftBoundGameObject.transform.position.y);
        m_RightBound = new Vector2(m_RightBoundGameObject.transform.position.x, m_RightBoundGameObject.transform.position.y);
        // 销毁边界对象,提升一点性能
        GameObject.Destroy(m_LeftBoundGameObject);
        GameObject.Destroy(m_RightBoundGameObject);
    }

    private void FixedUpdate()
    {
    
    
        Move();  // 移动
    }

    private void Update()
    {
    
    
        
    }

    // --- public ------------------------------------------

    /// <summary>
    /// @biref 移动
    /// </summary>
    public void Move()
    {
    
    
        HorizontalMove();  // 水平移动
    }

    // --- private -----------------------------------------

    /// <summary>
    /// @biref 水平移动
    /// </summary>
    private void HorizontalMove()
    {
    
    
        // 用边界判断转向
        if (!m_BTurnDirection && transform.position.x > m_RightBound.x)
        {
    
    
            m_BTurnDirection = true;
            transform.rotation = Quaternion.Euler(0.0f, 180.0f, 0.0f);
        }
        if (m_BTurnDirection && transform.position.x < m_LeftBound.x)
        {
    
    
            m_BTurnDirection = false;
            transform.rotation = Quaternion.Euler(0.0f, 0.0f, 0.0f);
        }

        if (m_BTurnDirection)
            m_Rb.velocity = new Vector2(-1.0f * m_HorizontalMovementSpeed * Time.fixedDeltaTime, m_Rb.velocity.y);
        else
            m_Rb.velocity = new Vector2(m_HorizontalMovementSpeed * Time.fixedDeltaTime, m_Rb.velocity.y);
    }
}

2. PlayerController.cs 改动(添加代码)

[Header("【检测相关】")]
public bool m_BInjuringLeft = false;                        // 判断 是否正在受伤(受到的力向左)
public bool m_BInjuringRight = false;                       // 判断 是否正在受伤(受到的力向右)

[Header("战斗相关")]
public Vector2 m_InjuredResistance;                         // 受伤时的反抗力,用来从受伤状态恢复正常状态

private void Start()
{
    
    
    // 获取组件
    m_Rb = GetComponent<Rigidbody2D>();
    m_CapsuleCollider2D = GetComponent<CapsuleCollider2D>();
    m_Animator = GetComponent<Animator>();

    // 设置参数
    m_CurrentAllowAirJumpCount = m_AllowAirJumpCount;
    m_CapsuleCollider2DSize = m_CapsuleCollider2D.size;
    m_CapsuleCollider2DOffset = m_CapsuleCollider2D.offset;

    // 设置受伤时反抗力
    m_InjuredResistance = new Vector2(10.0f, 0.0f);         // X,Y 方向都有
}

/// <summary>
/// @breif 移动控制
/// </summary>
public void MoveControl()
{
    
    
    if (!m_BInjuringLeft && !m_BInjuringRight)       // 如果正在受伤,不能控制移动
    {
    
    
        HorizontalMove();   // 水平移动
        Jump();             // 跳跃
        Crouch();           // 下蹲
    }
    else
    {
    
    
        // 暂时先默认 y 轴反抗力向下,x 轴的反抗力根据速度方向改变
        if (m_BInjuringLeft && m_Rb.velocity.x < 0.0f)
            m_Rb.velocity = new Vector2(m_Rb.velocity.x + m_InjuredResistance.x * Time.fixedDeltaTime, m_Rb.velocity.y - m_InjuredResistance.y);
        else
            m_BInjuringLeft = false;

        if (m_BInjuringRight && m_Rb.velocity.x > 0.0f)
            m_Rb.velocity = new Vector2(m_Rb.velocity.x - m_InjuredResistance.x * Time.fixedDeltaTime, m_Rb.velocity.y - m_InjuredResistance.y);
        else
            m_BInjuringRight = false;
    }
}

/// <summary>
/// @brief 动画控制
/// </summary>
public void AnimatorControl()
{
    
    
    m_Animator.SetFloat("HorizontalSpeedPerSecond", Mathf.Abs(m_Rb.velocity.x));
    m_Animator.SetBool("BIdling", m_BAnimIdling);
    m_Animator.SetBool("BJumping", m_BAnimJumping);
    m_Animator.SetBool("BFalling", m_BAnimFalling);
    m_Animator.SetBool("BCrouching", m_BAnimCrouching);
    m_Animator.SetBool("BInjuring", m_BInjuringLeft || m_BInjuringRight);
}

/// <summary>
/// @brief 碰撞开关检测
/// </summary>
/// <param name="collision"></param>
private void OnTriggerEnter2D(Collider2D collision)
{
    
    
    // 如果碰到的是 “收集物品”
    if (collision.tag == "Collections")
    {
    
    
        // 收集物品,然后销毁物品对象(脚本在物品身上,用的动画帧事件)
        m_CollectionsCount++;  // 收集物品数目加一

        // 物品播放反馈动画
        Animator collisionAnimator = collision.GetComponent<Animator>();
        collisionAnimator.SetBool("BFeedback", true);
    }

    // 如果碰到的是 “敌人 熊”
    if (collision.tag == "Enemy_Bear")
    {
    
    
        // 获取熊对象
        BearBase bear = collision.GetComponent<BearBase>();
        // 收到冲击
        if (transform.position.x < collision.transform.position.x)
        {
    
    
            m_BInjuringLeft = true;  // 改变受伤状态
            // 添加瞬间冲击力,类似爆炸的那种
            m_Rb.AddForce(new Vector2(-1.0f * bear.m_ForceOfRepelling, m_Rb.velocity.y), ForceMode2D.Impulse);
        }
        else
        {
    
    
            m_BInjuringRight = true;  // 改变受伤状态
            m_Rb.AddForce(new Vector2(bear.m_ForceOfRepelling, m_Rb.velocity.y), ForceMode2D.Impulse);
        }
    }
}

C. 动画部分

敌人熊只有一个行走动画,设置很简单,我们来为 玩家 添加一个受伤动画

动画器添加

D. PlayerController 完整代码

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

public class PlayerController : MonoBehaviour
{
    
    
    // --- public -----------------------------------
    public Rigidbody2D m_Rb;                                    // Player 刚体组件
    public CapsuleCollider2D m_CapsuleCollider2D;               // 胶囊碰撞体组件
    public Animator m_Animator;                                 // 动画控制器

    // --- private ----------------------------------
    /** 检测相关 */
    [Header("【只能看不能改】")]
    [SerializeField] private bool m_BTurnDirection = false;     // Player 是否转向
    [SerializeField] private bool m_BDoJump = false;            // 判断 是否执行跳跃相关
    [SerializeField] private bool m_BPressedCrouch = false;     // 判断 是否按下 下蹲键
    [SerializeField] private bool m_BOnGround = false;          // 判断 是否在地面
    [SerializeField] private int m_CurrentAllowAirJumpCount = 0;// 当前 允许跳空中跃次数
    [SerializeField] private Vector2 m_CapsuleCollider2DSize;   // 保存碰撞体 初始大小
    [SerializeField] private Vector2 m_CapsuleCollider2DOffset; // 保存碰撞体 初始偏移
    [SerializeField] private bool m_BTopHasWall = false;        // 上方是否有墙
    // 检测动画状态
    [SerializeField] private bool m_BAnimIdling = true;         // 闲置状态
    [SerializeField] private bool m_BAnimRunning = false;       // 跑动状态
    [SerializeField] private bool m_BAnimJumping = false;       // 跳跃状态
    [SerializeField] private bool m_BAnimFalling = false;       // 下落状态
    [SerializeField] private bool m_BAnimCrouching = false;     // 下蹲状态

    // --- Public -----------------------------------
    [Header("【移动参数】")]
    public float m_HorizontalSpeedFactorPerSecond = 300.0f;     // 水平移动速度
    public float m_JumpSpeedPerSecond = 520.0f;                 // 跳跃速度
    public int m_AllowAirJumpCount = 1;                         // 允许跳空中跃次数

    [Header("【检测相关】")]
    public LayerMask m_LMGround;                                // 地面图层蒙版
    public float m_CheckGround_Left_XOffset = -0.4f;            // 地面检测 射线 x 左偏移
    public float m_CheckGround_Right_XOffset = 0.2f;            // 地面检测 射线 x 右偏移
    public float m_CheckGround_YOffset = -0.95f;                // 地面检测 射线 y 偏移
    public float m_CheckGround_Distance = 0.1f;                 // 地面检测 射线发射距离
    public float m_CheckTopHasWall_Distance = 0.4f;             // 检测上方是否右墙 射线 距离
    public float m_CheckTopHasWall_Left_XOffset = -0.4f;        // 地面检测 射线 x 左偏移
    public float m_CheckTopHasWall_Right_XOffset = 0.35f;       // 地面检测 射线 x 右偏移
    public bool m_BInjuringLeft = false;                        // 判断 是否正在受伤(受到的力向左)
    public bool m_BInjuringRight = false;                       // 判断 是否正在受伤(受到的力向右)

    [Header("物品收集")]
    public int m_CollectionsCount = 0;                          // 收集到的物品数量

    [Header("战斗相关")]
    public Vector2 m_InjuredResistance;                         // 受伤时的反抗力,用来从受伤状态恢复正常状态

    // Start is called before the first frame update
    private void Start()
    {
    
    
        // 获取组件
        m_Rb = GetComponent<Rigidbody2D>();
        m_CapsuleCollider2D = GetComponent<CapsuleCollider2D>();
        m_Animator = GetComponent<Animator>();

        // 设置参数
        m_CurrentAllowAirJumpCount = m_AllowAirJumpCount;
        m_CapsuleCollider2DSize = m_CapsuleCollider2D.size;
        m_CapsuleCollider2DOffset = m_CapsuleCollider2D.offset;

        // 设置受伤时反抗力
        m_InjuredResistance = new Vector2(10.0f, 0.0f);         // X,Y 方向都有
    }

    private void FixedUpdate()
    {
    
    
        MoveControl();      // 移动控制
    }

    // Update is called once per frame
    private void Update()
    {
    
    
        Check();            // 检测
        AnimatorControl();  // 动画控制
    }

    // --- public -------------------------------------------

    /// <summary>
    /// @breif 移动控制
    /// </summary>
    public void MoveControl()
    {
    
    
        if (!m_BInjuringLeft && !m_BInjuringRight)       // 如果正在受伤,不能控制移动
        {
    
    
            HorizontalMove();   // 水平移动
            Jump();             // 跳跃
            Crouch();           // 下蹲
        }
        else
        {
    
    
            // 暂时先默认 y 轴反抗力向下,x 轴的反抗力根据速度方向改变
            if (m_BInjuringLeft && m_Rb.velocity.x < 0.0f)
                m_Rb.velocity = new Vector2(m_Rb.velocity.x + m_InjuredResistance.x * Time.fixedDeltaTime, m_Rb.velocity.y - m_InjuredResistance.y);
            else
                m_BInjuringLeft = false;

            if (m_BInjuringRight && m_Rb.velocity.x > 0.0f)
                m_Rb.velocity = new Vector2(m_Rb.velocity.x - m_InjuredResistance.x * Time.fixedDeltaTime, m_Rb.velocity.y - m_InjuredResistance.y);
            else
                m_BInjuringRight = false;
        }
    }
    
    /// <summary>
    /// @brief 检测部分
    /// </summary>
    public void Check()
    {
    
    
        CheckInput();       // 输入检测
        CheckOnGround();    // 检测是否在地面
        CheckTopHasWall();  // 检测头上是否有墙壁
        CheckState();       // 检测状态
    }

    /// <summary>
    /// @brief 动画控制
    /// </summary>
    public void AnimatorControl()
    {
    
    
        m_Animator.SetFloat("HorizontalSpeedPerSecond", Mathf.Abs(m_Rb.velocity.x));
        m_Animator.SetBool("BIdling", m_BAnimIdling);
        m_Animator.SetBool("BJumping", m_BAnimJumping);
        m_Animator.SetBool("BFalling", m_BAnimFalling);
        m_Animator.SetBool("BCrouching", m_BAnimCrouching);
        m_Animator.SetBool("BInjuring", m_BInjuringLeft || m_BInjuringRight);
    }

    // --- private ------------------------------------------

    /// <summary>
    /// @brief 碰撞开关检测
    /// </summary>
    /// <param name="collision"></param>
    private void OnTriggerEnter2D(Collider2D collision)
    {
    
    
        // 如果碰到的是 “收集物品”
        if (collision.tag == "Collections")
        {
    
    
            // 收集物品,然后销毁物品对象(脚本在物品身上,用的动画帧事件)
            m_CollectionsCount++;  // 收集物品数目加一

            // 物品播放反馈动画
            Animator collisionAnimator = collision.GetComponent<Animator>();
            collisionAnimator.SetBool("BFeedback", true);
        }

        // 如果碰到的是 “敌人 熊”
        if (collision.tag == "Enemy_Bear")
        {
    
    
            // 获取熊对象
            BearBase bear = collision.GetComponent<BearBase>();
            // 收到冲击
            if (transform.position.x < collision.transform.position.x)
            {
    
    
                m_BInjuringLeft = true;  // 改变受伤状态
                // 添加瞬间冲击力,类似爆炸的那种
                m_Rb.AddForce(new Vector2(-1.0f * bear.m_ForceOfRepelling, m_Rb.velocity.y), ForceMode2D.Impulse);
            }
            else
            {
    
    
                m_BInjuringRight = true;  // 改变受伤状态
                m_Rb.AddForce(new Vector2(bear.m_ForceOfRepelling, m_Rb.velocity.y), ForceMode2D.Impulse);
            }
        }
    }

    /// <summary>
    /// @brief 检测状态
    /// </summary>
    private void CheckState()
    {
    
    
        // 跑动
        m_BAnimRunning = (Mathf.Abs(m_Rb.velocity.x) > 0.1f && !m_BAnimCrouching && !m_BAnimJumping && !m_BAnimFalling);
        // 下落
        m_BAnimFalling = m_Rb.velocity.y < -0.1f;
        // 跳跃
        if ((m_BOnGround && m_BDoJump) || (!m_BOnGround && m_AllowAirJumpCount > 0 && m_BDoJump))
        {
    
    
            m_BAnimJumping = true;
        }
        else if (m_BAnimFalling)
        {
    
    
            m_BAnimJumping = false;
        }
        // 下蹲
        m_BAnimCrouching = (m_BOnGround && ((m_BPressedCrouch && (!m_BAnimJumping || !m_BAnimFalling)) || m_BTopHasWall));
        // 闲置
        m_BAnimIdling = (m_BOnGround && !m_BAnimCrouching && !m_BAnimFalling);
    }

    /// <summary>
    /// @brief 输入检测
    /// </summary>
    private void CheckInput()
    {
    
    
        // 检测跳跃键【GetButtonDown 这个函数,一直按下也只会算一次,需要松开再按才算下一次】
        //Debug.LogWarning(Input.GetButtonDown("Jump"));
        if (Input.GetButtonDown("Jump") && m_CurrentAllowAirJumpCount > 0)
        {
    
    
            // 如果可以跳跃,执行跳跃相关
            m_BDoJump = true;
        }
        // 检测下蹲键
        m_BPressedCrouch = Input.GetButton("Crouch");
    }

    /// <summary>
    /// @brief 检测上方是否有墙壁
    /// </summary>
    private void CheckTopHasWall()
    {
    
    
        float yOffset = -0.2f;  // 检测上方是否有墙壁的偏移量
        // 射线起点
        Vector2 leftStart2 = new Vector2(transform.position.x + m_CheckTopHasWall_Left_XOffset, transform.position.y + yOffset);
        Vector2 rightStart2 = new Vector2(transform.position.x + m_CheckTopHasWall_Right_XOffset, transform.position.y + yOffset);
        // 射线方向
        Vector2 direction2 = Vector2.up;
#if DEBUG  // 调试用变量
        Vector3 leftStart3 = new Vector3(transform.position.x + m_CheckTopHasWall_Left_XOffset, transform.position.y + yOffset, 0.0f);
        Vector3 rightStart3 = new Vector3(transform.position.x + m_CheckTopHasWall_Right_XOffset, transform.position.y + yOffset, 0.0f);
        Vector3 direction3 = Vector3.up;
#endif
        // 射线持续时间(Debug)
        float durationTime = 0.0f;
        RaycastHit2D leftHitResult = Physics2D.Raycast(leftStart2, direction2, m_CheckTopHasWall_Distance, m_LMGround);
        RaycastHit2D rightHitResult = Physics2D.Raycast(rightStart2, direction2, m_CheckTopHasWall_Distance, m_LMGround);

        if (leftHitResult || rightHitResult)
        {
    
    
            m_BTopHasWall = true;
            // Debug
#if DEBUG
            Debug.DrawLine(leftStart3, leftStart3 + direction3 * m_CheckTopHasWall_Distance, Color.green, durationTime);  // 绿
            Debug.DrawLine(rightStart3, rightStart3 + direction3 * m_CheckTopHasWall_Distance, Color.green, durationTime);  // 绿
#endif
        }
        else
        {
    
    
            m_BTopHasWall = false;
            // Debug
#if DEBUG
            Debug.DrawLine(leftStart3, leftStart3 + direction3 * m_CheckTopHasWall_Distance, Color.red, durationTime);  // 红
            Debug.DrawLine(rightStart3, rightStart3 + direction3 * m_CheckTopHasWall_Distance, Color.red, durationTime);  // 红
#endif
        }
    }

    /// <summary>
    /// @brief 检测是否在地面上
    /// </summary>
    private void CheckOnGround()
    {
    
    
        // 射线起点
        Vector2 leftStart2 = new Vector2(transform.position.x + m_CheckGround_Left_XOffset, transform.position.y + m_CheckGround_YOffset);
        Vector2 rightStart2 = new Vector2(transform.position.x + m_CheckGround_Right_XOffset, transform.position.y + m_CheckGround_YOffset);
        Vector2 leftTurnDirectionStart2 = new Vector2(leftStart2.x + 0.18f, leftStart2.y);
        Vector2 rightTurnDirectionStart2 = new Vector2(rightStart2.x + 0.22f, rightStart2.y);
#if DEBUG
        Vector3 leftStart3 = new Vector3(leftStart2.x, leftStart2.y, 0.0f);
        Vector3 rightStart3 = new Vector3(rightStart2.x, rightStart2.y, 0.0f);
        Vector3 leftTurnDirectionStart3 = new Vector3(leftTurnDirectionStart2.x, leftTurnDirectionStart2.y, 0.0f);
        Vector3 rightTurnDirectionStart3 = new Vector3(rightTurnDirectionStart2.x, rightTurnDirectionStart2.y, 0.0f);
#endif
        // 射线方向
        Vector2 direction2 = Vector2.down;
        Vector3 direction3 = Vector3.down;
        // 射线持续时间(Debug)
        float durationTime = 0.0f;
        // 射线结果,该结构体也有重写 bool operator,所以可以直接用来判断
        RaycastHit2D leftHitResult;
        RaycastHit2D rightHitResult;
        if (m_BTurnDirection)  // 如果转向了
        {
    
    
            leftHitResult = Physics2D.Raycast(leftTurnDirectionStart2, direction2, m_CheckGround_Distance, m_LMGround);
            rightHitResult = Physics2D.Raycast(rightTurnDirectionStart2, direction2, m_CheckGround_Distance, m_LMGround);
        }
        else  // 如果没转向
        {
    
    
            leftHitResult = Physics2D.Raycast(leftStart2, direction2, m_CheckGround_Distance, m_LMGround);
            rightHitResult = Physics2D.Raycast(rightStart2, direction2, m_CheckGround_Distance, m_LMGround);
        }

        if (leftHitResult.collider || rightHitResult.collider)
        {
    
    
            // 在地面上
            m_BOnGround = true;
            // 重置空中跳跃次数
            m_CurrentAllowAirJumpCount = m_AllowAirJumpCount;
            // 调试,击中为绿色
#if DEBUG
            if (m_BTurnDirection)
            {
    
    
                Debug.DrawLine(leftTurnDirectionStart3, leftTurnDirectionStart3 + direction3 * m_CheckGround_Distance, Color.green, durationTime);
                Debug.DrawLine(rightTurnDirectionStart3, rightTurnDirectionStart3 + direction3 * m_CheckGround_Distance, Color.green, durationTime);
            }
            else
            {
    
    
                Debug.DrawLine(leftStart3, leftStart3 + direction3 * m_CheckGround_Distance, Color.green, durationTime);
                Debug.DrawLine(rightStart3, rightStart3 + direction3 * m_CheckGround_Distance, Color.green, durationTime);
            }
#endif
        }
        else
        {
    
    
            // 不在地面上
            m_BOnGround = false;
            // 调试
#if DEBUG
            if (m_BTurnDirection)
            {
    
    
                Debug.DrawLine(leftTurnDirectionStart3, leftTurnDirectionStart3 + direction3 * m_CheckGround_Distance, Color.red, durationTime);
                Debug.DrawLine(rightTurnDirectionStart3, rightTurnDirectionStart3 + direction3 * m_CheckGround_Distance, Color.red, durationTime);
            }
            else
            {
    
    
                Debug.DrawLine(leftStart3, leftStart3 + direction3 * m_CheckGround_Distance, Color.red, durationTime);
                Debug.DrawLine(rightStart3, rightStart3 + direction3 * m_CheckGround_Distance, Color.red, durationTime);
            }
#endif
        }
    }

    /// <summary>
    /// @brief 水平移动
    /// </summary>
    private void HorizontalMove()
    {
    
    
        // 获取水平输入 [-1, 1],松开按键时为 0
        float horizontalValue = Input.GetAxis("Horizontal");
        m_Rb.velocity = new Vector2(horizontalValue * m_HorizontalSpeedFactorPerSecond * Time.fixedDeltaTime, m_Rb.velocity.y);

        // Player 转向
        if (horizontalValue < 0.0f)  // 默认是右边
        {
    
    
            m_BTurnDirection = true;
            transform.rotation = Quaternion.Euler(0.0f, 180.0f, 0.0f);
        }
        else if (horizontalValue > 0.0f)
        {
    
    
            m_BTurnDirection = false;
            transform.rotation = Quaternion.Euler(0.0f, 0.0f, 0.0f);
        }
    }

    /// <summary>
    /// @brief 跳跃
    /// </summary>
    private void Jump()
    {
    
    
        if (m_BOnGround && m_BDoJump)
        {
    
    
            m_BDoJump = false;
            m_Rb.velocity = new Vector2(m_Rb.velocity.x, m_JumpSpeedPerSecond * Time.fixedDeltaTime);
        }
        if (!m_BOnGround && m_CurrentAllowAirJumpCount > 0 && m_BDoJump)
        {
    
    
            // 空中跳跃
            m_BDoJump = false;
            --m_CurrentAllowAirJumpCount;
            m_Rb.velocity = new Vector2(m_Rb.velocity.x, m_JumpSpeedPerSecond * Time.fixedDeltaTime);
        }
    }

    /// <summary>
    /// @brief 下蹲
    /// </summary>
    private void Crouch()
    {
    
    
        if (m_BPressedCrouch)
        {
    
    
            m_CapsuleCollider2D.size = new Vector2(m_CapsuleCollider2D.size.x, 0.6f);
            m_CapsuleCollider2D.offset = new Vector2(m_CapsuleCollider2D.offset.x, -0.545f);
        }
        else if (!m_BTopHasWall)  // 上方如果检测到有墙不能起来
        {
    
    
            m_CapsuleCollider2D.size = m_CapsuleCollider2DSize;
            m_CapsuleCollider2D.offset = m_CapsuleCollider2DOffset;
        }
    }
}

E. 小结演示










[入门卷] 10. 简单音效

A. Audio Listener,Source,Clips

Audio Listener 有点像人的耳朵、收听者

这个一般在相机身上自带,我们只做简单的实现,所以暂时不用动

音频监听器通常附加到您要使用的摄像机

Audio Source 是声源,可以确定声音的位置,里面存储了声音片段

Audio Clips 是声音片段,可以用作替换 Audio Source 中的声音片段

B. 代码实现

创建一个新的脚本用来管理场景中的声音 AudioManager.cs

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

public class AudioManager : MonoBehaviour
{
    
    
    // --- public ----------------------------------
    public AudioSource m_AudioManager;
    public AudioClip m_AudioClipJump;
    public AudioClip m_AudioClipInjured;

    // --- private ---------------------------------
    private static AudioManager m_Instance = null;  // 单例
    private AudioManager() {
    
     }

    private void Start()
    {
    
    
        // 开始获取单例
        m_Instance = this;
    }

    // --- public ----------------------------------

    /// <summary>
    /// @brief 获取实例
    /// </summary>
    /// <returns></returns>
    public static AudioManager GetInstance()
    {
    
    
        if (m_Instance == null)
            m_Instance = new AudioManager();
        return m_Instance;
    }

    /// <summary>
    /// @brief 播放跳跃音效
    /// </summary>
    public void PlayJumpAudio()
    {
    
    
        m_AudioManager.clip = m_AudioClipJump;
        m_AudioManager.Play();
    }

    /// <summary>
    /// @brief 播放受伤音效
    /// </summary>
    public void PlayInjuredAudio()
    {
    
    
        m_AudioManager.clip = m_AudioClipInjured;
        m_AudioManager.Play();
    }
}

在 Hierarchy 面板中创建一个空对象命名为 AudioManager,然后添加组件

背景音乐用的是资源自带的,还有跳跃和受伤是网上搜的,这里不方便贴出来。有点麻烦…,挺好找的,Unity Store 也有很多,练习用没必要太纠结音乐

Ok,最后在 PlayerCotroller.cs 中添加脚本即可

/// <summary>
/// @brief 碰撞开关检测
/// </summary>
/// <param name="collision"></param>
private void OnTriggerEnter2D(Collider2D collision)
{
    
    
    // 如果碰到的是 “收集物品”
    if (collision.tag == "Collections")
    {
    
    
        // 收集物品,然后销毁物品对象(脚本在物品身上,用的动画帧事件)
        m_CollectionsCount++;  // 收集物品数目加一

        // 物品播放反馈动画
        Animator collisionAnimator = collision.GetComponent<Animator>();
        collisionAnimator.SetBool("BFeedback", true);
    }

    // 如果碰到的是 “敌人 熊”
    if (collision.tag == "Enemy_Bear")
    {
    
    
        // 播放音效
        AudioManager.GetInstance().PlayInjuredAudio();
        // 获取熊对象
        BearBase bear = collision.GetComponent<BearBase>();
        // 收到冲击
        if (transform.position.x < collision.transform.position.x)
        {
    
    
            m_BInjuringLeft = true;  // 改变受伤状态
            // 添加瞬间冲击力,类似爆炸的那种
            m_Rb.AddForce(new Vector2(-1.0f * bear.m_ForceOfRepelling, m_Rb.velocity.y), ForceMode2D.Impulse);
        }
        else
        {
    
    
            m_BInjuringRight = true;  // 改变受伤状态
            m_Rb.AddForce(new Vector2(bear.m_ForceOfRepelling, m_Rb.velocity.y), ForceMode2D.Impulse);
        }
    }
}

/// <summary>
/// @brief 跳跃
/// </summary>
private void Jump()
{
    
    
    if (m_BOnGround && m_BDoJump)
    {
    
    
        m_BDoJump = false;
        m_Rb.velocity = new Vector2(m_Rb.velocity.x, m_JumpSpeedPerSecond * Time.fixedDeltaTime);
        // 播放音效
        AudioManager.GetInstance().PlayJumpAudio();
    }
    if (!m_BOnGround && m_CurrentAllowAirJumpCount > 0 && m_BDoJump)
    {
    
    
        // 空中跳跃
        m_BDoJump = false;
        --m_CurrentAllowAirJumpCount;
        m_Rb.velocity = new Vector2(m_Rb.velocity.x, m_JumpSpeedPerSecond * Time.fixedDeltaTime);
        // 播放音效
        AudioManager.GetInstance().PlayJumpAudio();
    }
}

这里注意一些选项,运行游戏,你应该可以正常播放音乐

你可以将这个管理音乐的对象变成预制体,放在第二个场景中使用,可能只需要更改背景音乐的部分即可










[入门卷] 11. 简单光照

我们转到场景2,为场景添加光照,首先我们需要用到 UPR 插件,需要安装一下

A. 设置材质

设置 Tilemap 材质

B. 为 Player 创建材质

设置

C. 添加点光源

稍微设置一下 Z 轴,朝屏幕外为负

这里会发现网格又有裂缝了…,需要将 Grid 大小调回 1

这样就可以了

实在不行可以在 Project Settings 调节抗锯齿

D. 添加方向光

最后我还添加了方向光,参数可以自己试着调节

注意到这里我在 Player 身上挂了两个点光源
由于我在处理角色转向的逻辑时,使用的时旋转,而点光源作为子物体也会跟着旋转,
如果只挂一个,可能旋转转向后就看不见光了…
所以挂两个解决这个问题

最新更新,其实可以挂在相机身上,可以不用复制两份了

转向光照有点影响,可以稍微研究一下,这里更改缩放可以解决!

// Player 转向
if (horizontalValue < 0.0f)  // 默认是右边
{
    
    
    m_BTurnDirection = true;
    //transform.rotation = Quaternion.Euler(0.0f, 180.0f, 0.0f);
    transform.localScale = new Vector3(-1.0f, 1.0f, 1.0f);
}
else if (horizontalValue > 0.0f)
{
    
    
    m_BTurnDirection = false;
    //transform.rotation = Quaternion.Euler(0.0f, 0.0f, 0.0f);
    transform.localScale = new Vector3(1.0f, 1.0f, 1.0f);
}

OK,现在场景中的光照就简单布置完成了

E. 参数参考

F. 小节示例










三、最后

写到这里,发现还有很多没写,但是篇幅又变的很长了

所以之后例如 UI、生成项目等会在下卷介绍

那么,本卷的内容就到这里了,下卷会继续分享 Unity2D 游戏开发入门相关知识

The End.

猜你喜欢

转载自blog.csdn.net/weixin_50114337/article/details/128293370