Unity makes angry birds
- 1. Project preparation
- Two, slice
- 3. Realize the dragging and flying of the bird
- 4. Scene construction
- 5. Realize the bird attack pig
- 6. Realize the control of multiple birds
- 7. Implementation of bird trails
- 8. Build houses for pigs
- 9. Game victory and failure interface
- 10. Pause interface
- 11. Camera follow
- reference
Reference Video: [SiKi Academy Unity] Unity Elementary Case - Angry Birds
1. Project preparation
Resource download: http://www.sikiedu.com/course/134
New Construction:
Select 2D
, fill in the project name and select the project path:
Image
Copy the and from the resources Music
into the project folder:
Two, slice
Select the first chapter picture of pigs and birds, change it Sprite Mode
to Multiple
and Apply
:
Slice as shown below:
It is found that the smoke from the four explosions has not been segmented well, so manually segment it:
Apply
:
Drag the slingshot and bird into the scene, and set the hierarchical relationship:
3. Realize the dragging and flying of the bird
1. Add and set Spring Joint 2D
Add Spring Joint 2D
a component for the bird, and the rigid body component will be added automatically after adding:
At the same time, add a rigid body component to the right part of the slingshot and set it as Static
(to prevent it from being affected by gravity):
Drag the rigid body on the right side of the slingshot Spring Joint 2D
to , and set Spring Joint 2D
the parameters:
2. Implement the movement of the bird following the mouse
Add colliders and Bird
scripts:
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;
}
}
At this time, when the bird is dragged by the mouse, it will move with the mouse.
3. Limit the maximum dragging distance of the bird
First set a point on the right side of the slingshot as the point around which the bird turns:
Bird.cs
Declare the rightPos and maxDis variables in :
public Transform rightPos; // 弹弓右部点,即拖拽小鸟时让其跟着转
public float maxDis; // 最大推拽距离
Modify MoveWithMouse()
as follows:
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;
}
}
}
Drag it into the script in Inspector
the panel and set it . At this time, the bird is limited to the dragging distance:rightPos
bird
maxDis
4. Realize the bird flying out
On mouse down, the rigid body state is set to dynamic.
On mouseup, dynamics is set to false, and SpringJoint2D
the component is disabled after a certain amount of time:
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. Realize the scribing of the slingshot
Create a point on the slingshot leftPos
, at this point leftPos
and rightPos
two points to draw the line:
To left
add Line Renderer
components:
Set material, color and length:
Copy that component over right
:
Write code:
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);
}
}
At this time, when dragging the bird, the line can be drawn normally, and the line will disappear when the bird is released.
6. Prevent the bird from dragging repeatedly
At this time, after the bird is dragged and released, press and hold the bird again to return to the slingshot.
To fix this bug, add a bool parameter to the bird:
private bool flied = false; // 是否飞过,用以让小鸟不能重复拖拽
After the mouse is lifted, set it to true:
// 鼠标抬起
private void OnMouseUp()
{
isClick = false;
flied = true;
rb.isKinematic = false;
DeleteLine(); // 删除弹弓的线
Invoke(nameof(Fly), 0.12f);
}
After the mouse is pressed, if it flied
is already true, it has no effect:
// 鼠标按下
private void OnMouseDown()
{
if (flied)
return;
isClick = true;
rb.isKinematic = true;
}
At this point the bird can only be dragged once.
4. Scene construction
Select scene 1 for cutting:
Drag the ground in and add a collider:
Create prefab-related folders and drag them into:
Set the ground:
Similarly, set the sky:
5. Realize the bird attack pig
Add pig, and add related components and set layers:
Set the corner resistance of both the bird and the pig to 2 to prevent them from rolling on the ground without stopping:
1. Injuries and deaths of pigs
Add Pig
script to pig:
Set the relative speed at which the pig should be injured and killed when it is hit by a bird:
public float hurtSpeed = 5.0f; // 受伤速度
public float deadSpeed = 10.0f; // 死亡速度
Set the picture after the pig is injured:
// 受伤图片
public Sprite hurtSprite;
Drag the picture of the corresponding smoke into the scene:
Create boom
an animation:
Open Animation
and adjust accordingly:
At the same time cancel the loop of the animation:
Boom
Add a script for Boom
:
Write the code:
Boom.cs
:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Boom : MonoBehaviour
{
public void DestorySelf()
{
Destroy(gameObject);
}
}
Set in Animation
, execute this function after playing, that is, destroy itself:
Make Boom
an object into a prefab:
Pig death logic:
If the relative speed is greater than the death speed: it will die;
if the relative speed is only greater than the injury speed: if the pig is injured at this time, it will die; otherwise, it will be injured
In Pig.cs
, realize the injury and death of pigs:
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. Bonus points for pigs
Cut fractional image:
Drag out a score and make it into a prefab:
Write pig.cs
the code:
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);
}
}
6. Realize the control of multiple birds
Make the bird as a prefab and make two copies:
Create an Game Manager
empty object, create a new one and hang GameManager
the script:
Use the singleton pattern and store the birds and pigs in lists:
public class GameManager : MonoBehaviour
{
public static GameManager instance;
public List<Bird> birds;
public List<Pig> pigs;
private void Awake()
{
instance = this;
}
}
Write in Bird
, let the bird fly out for a few seconds and destroy it, generate an explosion animation, and GameManager
remove it from the list:
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);
}
Similarly, when the pig dies, GameManger
it also needs to be deleted from the list:
// 死亡,生成爆炸动画和分数
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);
}
In GameManager
, initially enable the first bird, disable other bird scripts and SpringJoint, and judge the game state after the bird is destroyed and whether to enable the next bird:
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();
}
}
}
}
After the bird is destroyed, call `Next()`: `bird.cs`:
// 飞出5秒后销毁
private void DestroySelf()
{
GameManager.instance.birds.Remove(this);
GameManager.instance.Next();
Instantiate(boomPrefab, transform.position, Quaternion.identity);
Destroy(gameObject);
}
Finally, Inspector
after dragging the birds and pigs in the panel, multiple birds can be controlled normally.
7. Implementation of bird trails
Import the Unity package in the material:
Just add Weapon Trail
:
Add Trail Renderer
components to the bird:
Set the trailing material, duration and width:
Simultaneously load on other prefabs:
Trailing effect:
8. Build houses for pigs
cutting:
Take square wood as an example:
Add bool's isPig to Pig.cs
the script so that wooden blocks can also be used:
[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);
}
Check the previous pigs isPig
:
Mount the script on the wood without checking it isPig
, and then set parameters such as hurtSpeed:
Wood needs components: Rigidbody, Collider, Pig script
Similarly, add other items.
A simple scenario to set up is as follows:
running result:
9. Game victory and failure interface
1. Display the basic interface of victory and defeat
cutting:
Make UI (refer to Chapter 15 ).
Lose UI
:
Win UI
:
When the game wins, judge the number of stars that should be obtained according to the number of remaining birds in the level. GameManager.cs
Part of the code is as follows:
[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颗
{
}
}
Meanwhile, for a script Win UI
that creates one Win.cs
:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Win : MonoBehaviour
{
// 动画播放完(显示UI后) 显示星星
public void ShowStar()
{
GameManager.instance.WinLevel();
}
}
Called after the animation ends:
2. Star particle effect
Cutting stars:
Placement of three stars:
Set the particle effect of the stars according to episodes 16 to 18 of https://www.bilibili.com/video/BV1qb411c76x?p=16 .
3. Star display
In GameManager.cs
, use the coroutine to display a star every 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
Drag stars into the panel :
At this time, the stars are displayed normally:
10. Pause interface
1. Interface and animation
Pause button:
Pause panel:
Implement pause animation:
Implement the continuation animation:
Both animations are unlooped:
2. Implement pause and resume
Animator
Create two parameters in Trigger
and connect the two animations:
The connection between Pause animation and Resume:
Create PausePanle
a script and put 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
Add code in:
[Space]
public GameObject pausePanel;
// 暂停按钮调用函数
public void PauseGame()
{
pausePanel.SetActive(true);
}
In Inspector
the panel, drag in Pause Panel
:
To Pause Btn
add a click event, for GameManager
the pause method:
To Resume Btn
add a click event, for Pause Panel
the Pause
method:
At Resume
the end of the animation, call AfterResume
the function:
Effect:
3. Implement Restart Level
Add the restart level method in GameManager.cs
:
using UnityEngine.SceneManagement;
// 重启关卡
public void RestartLevel()
{
SceneManager.LoadScene(SceneManager.GetActiveScene().buildIndex);
}
Called in each restart button:
running result:
11. Camera follow
1. Follow the first bird
open Package Manager
:
download Cinemachine
:
Add a 2D Camera
:
Drag in the first bird Follow
and let the camera follow the bird:
Adjust some parameters to choose a suitable position:
At the same time, create an Camera BG
empty object named, add Polygon
a collision body to it, check it Is Trigger
, and use it as the range of the camera (that is, if the bird exceeds this range, the camera will not follow):
In just now CM vcam1
, add CinemachineConfiner
:
Just Camera BG
drag in:
2. Follow multiple birds in sequence
Introduced in GameManager
:
using Cinemachine;
If an error is reported, import Cinemachine
the sample program scene:
GameManager.cs
Changed code:
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();
}
}
}
The final demo effect: