【Unity2D游戏开发入门第二卷】✨Unity入门总结Sunnyland示例(中卷)
✨✨目录
一、入门卷【中卷】
[入门卷] 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. 参数参考
![](https://img2023.cnblogs.com/blog/2805185/202212/2805185-20221212210549600-1811846723.png)
![](https://img2023.cnblogs.com/blog/2805185/202212/2805185-20221212210552387-80586433.png)
![](https://img2023.cnblogs.com/blog/2805185/202212/2805185-20221212210557090-1933807010.png)
## F. 小节示例
![](https://img2023.cnblogs.com/blog/2805185/202212/2805185-20221212210932430-1623198313.gif)
<br><br><br><br><br><br><br><br><br>
# <div id=end>三、最后<div>
写到这里,发现还有很多没写,但是篇幅又变的很长了
所以之后例如 UI、生成项目等会在下卷介绍
那么,本卷的内容就到这里了,下卷会继续分享 Unity2D 游戏开发入门相关知识
The End.