参考视频: 【SiKi学院Unity】Unity初级案例 - 愤怒的小鸟
一、项目准备
资源下载: http://www.sikiedu.com/course/134
新建工程:
选择2D
,填写项目名称并选择项目路径:
将资源中的Image
和Music
复制到项目文件夹中:
二、切片
选择猪和鸟的第一章图片,将其Sprite Mode
改为Multiple
并Apply
:
如下图所示进行切片:
发现四个爆炸烟雾未切分好,手动切分一下:
Apply
:
在场景中拖入弹弓和小鸟,设置层级关系:
三、实现小鸟的拖拽与飞出
1. 添加并设置Spring Joint 2D
为小鸟添加Spring Joint 2D
组件,添加后会自动添加刚体组件:
同时给弹弓右部加上刚体组件,并设置为Static
(防止其受重力影响):
将弹弓右部的刚体拖到Spring Joint 2D
中,并设置Spring Joint 2D
的各项参数:
2. 实现小鸟跟随鼠标移动
添加碰撞体和Bird
脚本:
bird.cs
:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Bird : MonoBehaviour
{
private bool isClick = false;
private void Update()
{
MoveWithMouse();
}
private void MoveWithMouse()
{
// 让小鸟跟随鼠标的位置
if (isClick)
{
transform.position = Camera.main.ScreenToWorldPoint(Input.mousePosition);
transform.position += new Vector3(0, 0, -Camera.main.transform.position.z);
}
}
// 鼠标按下
private void OnMouseDown()
{
isClick = true;
}
// 鼠标抬起
private void OnMouseUp()
{
isClick = false;
}
}
此时小鸟被鼠标拖拽时会跟随鼠标移动。
3. 限定小鸟最大拖拽距离
首先在弹弓右部设置一个点,作为小鸟绕着转的点:
在Bird.cs
中声明rightPos 和 maxDis变量:
public Transform rightPos; // 弹弓右部点,即拖拽小鸟时让其跟着转
public float maxDis; // 最大推拽距离
修改MoveWithMouse()
如下:
private void MoveWithMouse()
{
// 让小鸟跟随鼠标的位置
if (isClick)
{
transform.position = Camera.main.ScreenToWorldPoint(Input.mousePosition);
transform.position += new Vector3(0, 0, -Camera.main.transform.position.z);
// 限定小鸟最大拖拽距离
if (Vector2.Distance(transform.position, rightPos.position) >= maxDis)
{
transform.position = rightPos.position + (transform.position - rightPos.position).normalized * maxDis;
}
}
}
在Inspector
面板中将rightPos
拖拽到bird
脚本中,并设置maxDis
,此时小鸟被限定了拖拽距离:
4. 实现小鸟飞出
鼠标按下时,刚体状态设置为动力学。
在鼠标松开时,动力学设为false,一段时间后禁用SpringJoint2D
组件:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Bird : MonoBehaviour
{
private SpringJoint2D springJoint;
private Rigidbody2D rb;
public Transform rightPos; // 弹弓右部点,即拖拽小鸟时让其跟着转
public float maxDis; // 最大推拽距离
private bool isClick = false;
private void Start()
{
springJoint = GetComponent<SpringJoint2D>();
rb = GetComponent<Rigidbody2D>();
}
private void Update()
{
MoveWithMouse();
}
private void MoveWithMouse()
{
// 让小鸟跟随鼠标的位置
if (isClick)
{
transform.position = Camera.main.ScreenToWorldPoint(Input.mousePosition);
transform.position += new Vector3(0, 0, -Camera.main.transform.position.z);
// 限定小鸟最大拖拽距离
if (Vector2.Distance(transform.position, rightPos.position) >= maxDis)
{
transform.position = rightPos.position + (transform.position - rightPos.position).normalized * maxDis;
}
}
}
// 鼠标按下
private void OnMouseDown()
{
isClick = true;
rb.isKinematic = true;
}
// 鼠标抬起
private void OnMouseUp()
{
isClick = false;
rb.isKinematic = false;
Invoke(nameof(Fly), 0.12f);
}
private void Fly()
{
springJoint.enabled = false;
}
}
5. 实现弹弓的划线
在弹弓上创建一个leftPos
点,此时leftPos
和rightPos
两个点用于划线:
给left
添加Line Renderer
组件:
设置材质、颜色和长度:
将该组件复制到right
上:
编写代码:bird.cs
:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Bird : MonoBehaviour
{
private SpringJoint2D springJoint;
private Rigidbody2D rb;
[Header("弹弓")]
public Transform rightPos; // 弹弓右部点,即拖拽小鸟时让其跟着转; 同时画线
public Transform leftPos; // 弹弓左部点, 画线
public LineRenderer leftLine;
public LineRenderer rightLine;
[Space]
public float maxDis; // 最大推拽距离
private bool isClick = false;
private void Start()
{
springJoint = GetComponent<SpringJoint2D>();
rb = GetComponent<Rigidbody2D>();
}
private void Update()
{
MoveWithMouse();
}
private void MoveWithMouse()
{
// 让小鸟跟随鼠标的位置
if (isClick)
{
transform.position = Camera.main.ScreenToWorldPoint(Input.mousePosition);
transform.position += new Vector3(0, 0, -Camera.main.transform.position.z);
// 限定小鸟最大拖拽距离
if (Vector2.Distance(transform.position, rightPos.position) >= maxDis)
{
transform.position = rightPos.position + (transform.position - rightPos.position).normalized * maxDis;
}
DrawLine();
}
}
// 鼠标按下
private void OnMouseDown()
{
isClick = true;
rb.isKinematic = true;
}
// 鼠标抬起
private void OnMouseUp()
{
isClick = false;
rb.isKinematic = false;
DeleteLine(); // 删除弹弓的线
Invoke(nameof(Fly), 0.12f);
}
// 飞出
private void Fly()
{
springJoint.enabled = false;
}
// 画线
private void DrawLine()
{
// 设置线的两个端点
leftLine.SetPosition(0, leftPos.position);
leftLine.SetPosition(1, transform.position);
rightLine.SetPosition(0, rightPos.position);
rightLine.SetPosition(1, transform.position);
}
// 删除线
private void DeleteLine()
{
leftLine.SetPosition(1, leftPos.position);
rightLine.SetPosition(1, rightPos.position);
}
}
此时拖拽小鸟时可正常划线,松开则线消失。
6. 让小鸟不能重复拖拽
此时小鸟被拖拽放飞后,再次按住小鸟任然能回到弹弓上。
为了解决该bug,给小鸟添加一个bool参数:
private bool flied = false; // 是否飞过,用以让小鸟不能重复拖拽
鼠标抬起后,设置为true:
// 鼠标抬起
private void OnMouseUp()
{
isClick = false;
flied = true;
rb.isKinematic = false;
DeleteLine(); // 删除弹弓的线
Invoke(nameof(Fly), 0.12f);
}
鼠标按下后,如果flied
已经为true,则无效:
// 鼠标按下
private void OnMouseDown()
{
if (flied)
return;
isClick = true;
rb.isKinematic = true;
}
此时小鸟只能被拖拽一次。
四、场景搭建
选择场景1进行切割:
将地面拖入并添加碰撞体:
创建预制体相关文件夹,并将其拖入:
设置地面:
同理,设置天空:
五、实现小鸟攻击猪
添加猪,并添加相关组件并设置图层:
将鸟和猪的角阻力都设置为2,防止其在地面滚动不停止:
1. 猪的受伤与死亡
给猪添加Pig
脚本:
设置猪受到小鸟的碰撞时,受伤和死亡应该达到的相对速度:
public float hurtSpeed = 5.0f; // 受伤速度
public float deadSpeed = 10.0f; // 死亡速度
设置猪受伤后的图片:
// 受伤图片
public Sprite hurtSprite;
拖入相应的烟雾的图片到场景中:
创建boom
动画:
打开Animation
进行相应的调整:
同时将该动画取消循环播放:
为Boom
添加一个脚本Boom
:
编写代码:Boom.cs
:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Boom : MonoBehaviour
{
public void DestorySelf()
{
Destroy(gameObject);
}
}
在Animation
中设置,当播放完之后执行该函数,即销毁自身:
将Boom
物体制成预制体:
猪的死亡逻辑:
若相对速度大于死亡速度:则死亡;
若相对速度只是大于受伤速度:此时猪若是受伤状态,则死亡;反之则受伤
在Pig.cs
中,实现猪的受伤与死亡:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Pig : MonoBehaviour
{
private SpriteRenderer spriteRenderer;
// 受伤图片
public Sprite hurtSprite;
public GameObject boomPrefab;
[Header("相对速度")]
public float hurtSpeed = 5.0f; // 受伤速度
public float deadSpeed = 10.0f; // 死亡速度
private bool isHurt = false;
private void Start()
{
spriteRenderer = GetComponent<SpriteRenderer>();
}
private void OnCollisionEnter2D(Collision2D collision)
{
float relativeV = collision.relativeVelocity.magnitude;
print(relativeV);
// 大于死亡速度
if (relativeV >= deadSpeed)
{
Dead();
}
// 大于受伤速度
else if (relativeV >= hurtSpeed)
{
if (isHurt)
Dead();
else
{
isHurt = true;
spriteRenderer.sprite = hurtSprite;
}
}
}
// 死亡,生成爆炸动画和分数
private void Dead()
{
Instantiate(boomPrefab, transform.position, Quaternion.identity);
Destroy(gameObject);
}
}
2. 猪的加分
切割分数图片:
拖拽出一个分数并制为预制体:
编写pig.cs
代码:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Pig : MonoBehaviour
{
private SpriteRenderer spriteRenderer;
// 受伤图片
public Sprite hurtSprite;
public GameObject boomPrefab;
public GameObject scorePrefab;
[Header("相对速度")]
public float hurtSpeed = 5.0f; // 受伤速度
public float deadSpeed = 10.0f; // 死亡速度
private bool isHurt = false;
[Space]
public float scoreYOffset = 0.65f; // 分数相对猪的Y位置
private void Start()
{
spriteRenderer = GetComponent<SpriteRenderer>();
}
private void OnCollisionEnter2D(Collision2D collision)
{
float relativeV = collision.relativeVelocity.magnitude;
print(relativeV);
// 大于死亡速度
if (relativeV >= deadSpeed)
{
Dead();
}
// 大于受伤速度
else if (relativeV >= hurtSpeed)
{
if (isHurt)
Dead();
else
{
isHurt = true;
spriteRenderer.sprite = hurtSprite;
}
}
}
// 死亡,生成爆炸动画和分数
private void Dead()
{
GameManager.instance.pigs.Remove(this);
Instantiate(boomPrefab, transform.position, Quaternion.identity);
GameObject scoreObject = Instantiate(scorePrefab, transform.position + new Vector3(0, scoreYOffset, 0),
Quaternion.identity);
Destroy(scoreObject, 1.5f);
Destroy(gameObject);
}
}
六、实现多只鸟的控制
将小鸟制为预制体,并复制两只:
创建一个Game Manager
空物体,新建并挂上GameManager
脚本:
使用单例模式并用列表存储小鸟和猪:
public class GameManager : MonoBehaviour
{
public static GameManager instance;
public List<Bird> birds;
public List<Pig> pigs;
private void Awake()
{
instance = this;
}
}
在Bird
中编写,让小鸟飞出几秒后销毁,并生成爆炸动画,并从GameManager
的列表中移除:
private bool isClick = false;
private bool flied = false; // 是否飞过,用以让小鸟不能重复拖拽
// 飞出
private void Fly()
{
springJoint.enabled = false;
Invoke(nameof(DestroySelf), flyTime);
}
// 飞出5秒后销毁
private void DestroySelf()
{
GameManager.instance.birds.Remove(this);
Instantiate(boomPrefab, transform.position, Quaternion.identity);
Destroy(gameObject);
}
同理,猪死亡时,在GameManger
的列表中也需要删除:
// 死亡,生成爆炸动画和分数
private void Dead()
{
GameManager.instance.pigs.Remove(this);
Instantiate(boomPrefab, transform.position, Quaternion.identity);
GameObject scoreObject = Instantiate(scorePrefab, transform.position + new Vector3(0, scoreYOffset, 0),
Quaternion.identity);
Destroy(scoreObject, 1.5f);
Destroy(gameObject);
}
在GameManager
中,起初启用第一只小鸟、禁用其他小鸟的脚本和SpringJoint,并在小鸟销毁后判断游戏状态以及是否启用下一只小鸟:GameManager.cs
:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class GameManager : MonoBehaviour
{
public static GameManager instance;
public List<Bird> birds;
public List<Pig> pigs;
private Vector3 originBirdPos; // 小鸟初始位置
private void Awake()
{
instance = this;
if (birds.Count > 0)
originBirdPos = birds[0].transform.position;
InitBird();
}
// 初始化小鸟
private void InitBird()
{
for(int i = 0; i <birds.Count; i++)
{
if (i == 0)
{
birds[i].transform.position = originBirdPos;
birds[i].enabled = true;
birds[i].GetComponent<SpringJoint2D>().enabled = true;
}
else
{
birds[i].enabled = false;
birds[i].GetComponent<SpringJoint2D>().enabled = false;
}
}
}
// 判断游戏状态 及 是否启用下一只小鸟
public void Next()
{
if (pigs.Count == 0)
{
// 游戏胜利
}
else
{
if (birds.Count == 0)
{
// 游戏失败
}
else
{
InitBird();
}
}
}
}
在小鸟销毁后,调用`Next()`: `bird.cs`:
// 飞出5秒后销毁
private void DestroySelf()
{
GameManager.instance.birds.Remove(this);
GameManager.instance.Next();
Instantiate(boomPrefab, transform.position, Quaternion.identity);
Destroy(gameObject);
}
最后在Inspector
面板中拖拽小鸟和猪后,可正常控制多只小鸟。
七、小鸟尾迹的实现
导入素材中的Unity包:
只添加Weapon Trail
:
给小鸟添加Trail Renderer
组件:
设置拖尾的材质、持续时间已经宽度:
同时加载到其他预制体上:
拖尾效果:
八、给猪造房子
切割:
以方形木头为例:
在Pig.cs
脚本中添加bool的isPig,让木块等也能使用:
[Space]
public bool isPig = false;
// 死亡,生成爆炸动画和分数
private void Dead()
{
if (isPig)
GameManager.instance.pigs.Remove(this);
Instantiate(boomPrefab, transform.position, Quaternion.identity);
GameObject scoreObject = Instantiate(scorePrefab, transform.position + new Vector3(0, scoreYOffset, 0),
Quaternion.identity);
Destroy(scoreObject, 1.5f);
Destroy(gameObject);
}
将之前的猪勾选isPig
:
将木头挂载该脚本并不勾选isPig
,然后设置hurtSpeed等参数 :
木头需要组件: 刚体、碰撞体、Pig脚本
同理,新增其他物品。
设置简单的场景如下:
运行效果:
九、游戏胜利、失败界面
1. 显示胜利、失败基本界面
切割:
制作UI(参考第15集).
Lose UI
:
Win UI
:
在游戏胜利时,根据关卡中的剩余小鸟数量,判断应该获得的星星数量,GameManager.cs
部分代码如下:
[Header("UI")]
public GameObject winUI;
public GameObject loseUI;
[Header("胜利得到星星的数量需要的小鸟存活数")]
public int birdNumOf3Star;
public int birdNumOf2Star;
// 判断游戏状态 及 是否启用下一只小鸟
public void Next()
{
if (pigs.Count == 0)
{
// 游戏胜利
winUI.SetActive(true);
}
else
{
if (birds.Count == 0)
{
// 游戏失败
loseUI.SetActive(true);
}
else
{
InitBird();
}
}
}
public void WinLevel()
{
if (birds.Count >= birdNumOf3Star) // 3颗星星
{
}
else if (birds.Count >= birdNumOf2Star) // 2颗
{
}
else // 1颗
{
}
}
同时,为Win UI
创建一个Win.cs
的脚本:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Win : MonoBehaviour
{
// 动画播放完(显示UI后) 显示星星
public void ShowStar()
{
GameManager.instance.WinLevel();
}
}
在动画结束后调用:
2. 星星粒子效果
切割星星:
三颗星星的摆放:
根据https://www.bilibili.com/video/BV1qb411c76x?p=16第16到18集设置星星的粒子效果。
3. 星星显示
在GameManager.cs
中,利用协程,每隔0.7s,显示一颗星星:
public GameObject[] starsUI = new GameObject[3];
public void WinLevel()
{
if (birds.Count >= birdNumOf3Star) // 3颗星星
{
StartCoroutine("ShowTheStar", 3);
}
else if (birds.Count >= birdNumOf2Star) // 2颗
{
StartCoroutine("ShowTheStar", 2);
}
else // 1颗
{
StartCoroutine("ShowTheStar", 1);
}
}
// 每隔0.7秒,显示一颗星星
IEnumerator ShowTheStar(int num)
{
for(int i = 0; i < num; i++)
{
starsUI[i].SetActive(true);
yield return new WaitForSeconds(0.7f);
}
}
在Insepctor
面板中拖入星星:
此时正常显示星星:
十、暂停界面
1. 界面以及动画
P20
暂停按钮:
暂停面板:
实现暂停动画:
实现继续动画:
两个动画都取消循环播放:
2. 实现暂停和继续
在Animator
中创建两个Trigger
参数,并将两个动画相连:
Pause动画与Resume的连线:
创建PausePanle
脚本并放入Pause Panel
:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PausePanel : MonoBehaviour
{
private Animator anim;
// Start is called before the first frame update
void Start()
{
anim = GetComponent<Animator>();
}
// 点击继续按钮
public void Resume()
{
anim.SetTrigger("resume");
}
// 继续动画结束后,调用
public void AfterResume()
{
this.gameObject.SetActive(false);
anim.SetTrigger("pause"); // 切换回暂停动画,下次active true时则调用暂停动画
}
}
GameManager.cs
中添加代码:
[Space]
public GameObject pausePanel;
// 暂停按钮调用函数
public void PauseGame()
{
pausePanel.SetActive(true);
}
在Inspector
面板中,拖入Pause Panel
:
给Pause Btn
添加点击事件,为GameManager
的暂停方法:
给Resume Btn
添加点击事件,为Pause Panel
的Pause
方法:
在Resume
动画结束时,调用AfterResume
函数:
效果:
3. 实现Restart Level
在GameManager.cs
中添加重启关卡方法:
using UnityEngine.SceneManagement;
// 重启关卡
public void RestartLevel()
{
SceneManager.LoadScene(SceneManager.GetActiveScene().buildIndex);
}
在各个重启按钮中调用:
运行效果:
十一、相机跟随
1. 跟随第一只小鸟
打开Package Manager
:
下载Cinemachine
:
添加一个2D Camera
:
将第一只小鸟拖入Follow
,让相机跟随小鸟:
调节部分参数,以选取合适位置:
同时创建一个名为Camera BG
的空物体,为其加上Polygon
碰撞体,勾选Is Trigger
,用作相机的范围,(即小鸟若超出该范围,相机则不跟随):
在刚刚的CM vcam1
中,添加CinemachineConfiner
:
将刚刚的Camera BG
拖入:
2. 按顺序跟随多只小鸟
在GameManager
中引入:
using Cinemachine;
若报错,则导入Cinemachine
示例程序场景:
GameManager.cs
更改的代码:
using Cinemachine;
[Header("相机和弹弓")]
public CinemachineVirtualCamera virtualCamera;
public Transform slingshotLeftPos; // 弹弓左部点,用于小鸟死后视角回到弹弓
// 初始化小鸟
private void InitBird()
{
for(int i = 0; i <birds.Count; i++)
{
if (i == 0)
{
birds[i].transform.position = originBirdPos;
birds[i].enabled = true;
birds[i].GetComponent<SpringJoint2D>().enabled = true;
virtualCamera.Follow = birds[i].transform; // 相机跟随小鸟
}
else
{
birds[i].enabled = false;
birds[i].GetComponent<SpringJoint2D>().enabled = false;
}
}
}
// 判断游戏状态 及 是否启用下一只小鸟
public void Next()
{
virtualCamera.Follow = slingshotLeftPos;
virtualCamera.transform.position = slingshotLeftPos.position;
if (pigs.Count == 0)
{
// 游戏胜利
winUI.SetActive(true);
}
else
{
if (birds.Count == 0)
{
// 游戏失败
loseUI.SetActive(true);
}
else
{
InitBird();
}
}
}
最终演示效果:
文章来源:https://www.toymoban.com/news/detail-447180.html
参考
【SiKi学院Unity】Unity初级案例 - 愤怒的小鸟文章来源地址https://www.toymoban.com/news/detail-447180.html
到了这里,关于Unity 制作愤怒的小鸟的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!