Unity は怒れる鳥を作る
参考動画: [SiKi Academy Unity] Unity Elementary Case - Angry Birds
1. プロジェクトの準備
リソースのダウンロード: http://www.sikiedu.com/course/134
新築:
を選択し2D
、プロジェクト名を入力して、プロジェクトのパスを選択します。
リソースからImage
と をMusic
プロジェクト フォルダーにコピーします。
2、スライス
最初の章の豚と鳥の画像を選択し、次のようSprite Mode
に変更します。Multiple
Apply
以下のようにスライスします。
4 つの爆発による煙が適切にセグメント化されていないことが判明したため、手動でセグメント化します。
Apply
:
スリングショットと鳥をシーンにドラッグし、階層関係を設定します。
3. 鳥の引きずりや飛行を実現
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
この点に点を 1 つ作成しleftPos
、rightPos
線を引く 2 つの点を作成します。
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. 鳥が繰り返し引きずるのを防ぎます
このとき、鳥をドラッグして放した後、もう一度鳥を長押しするとスリングショットに戻ります。
このバグを修正するには、bird に 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 回だけです。
4. シーンの構築
カットするシーン 1 を選択します。
地面をドラッグしてコライダーを追加します。
プレハブ関連のフォルダーを作成し、次の場所にドラッグします。
地面を設定します。
同様に、空を設定します。
5.鳥の攻撃豚を実現する
豚を追加し、関連コンポーネントを追加してレイヤーを設定します。
鳥と豚が停止せずに地面を転がることを防ぐために、鳥と豚の両方の角の抵抗を 2 に設定します。
1. 豚の負傷と死亡
Pig
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);
}
}
6.複数の鳥の制御を実現
鳥をプレハブとして作成し、コピーを 2 つ作成します。
空のオブジェクトを作成し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
鳥と豚をパネル内にドラッグすると、複数の鳥を正常に制御できるようになります。
7. バードトレイルの実施
マテリアルに Unity パッケージをインポートします。
を追加するだけですWeapon Trail
:
Trail Renderer
コンポーネントを鳥に追加します。
末尾の素材、長さ、幅を設定します。
他のプレハブを同時にロードします。
後続効果:
8. 豚の家を建てる
切断:
角材を例に挙げます。
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
、hartSpeed などのパラメータを設定します。
Wood にはコンポーネントが必要です: Rigidbody、Collider、Pig スクリプト
同様に他の項目も追加していきます。
セットアップする簡単なシナリオは次のとおりです。
実行結果:
9. ゲームの勝利と失敗のインターフェース
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.スター粒子効果
星を切る:
3 つの星の配置:
https://www.bilibili.com/video/BV1qb411c76x?p=16のエピソード 16 ~ 18に従って星のパーティクル エフェクトを設定します。
3. スター表示
ではGameManager.cs
、コルーチンを使用して 0.7 秒ごとに星を表示します。
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
星をパネルにドラッグします。
この時点では、星は通常どおりに表示されます。
10.一時停止インターフェイス
1. インターフェースとアニメーション
一時停止ボタン:
一時停止パネル:
一時停止アニメーションを実装します。
継続アニメーションを実装します。
どちらのアニメーションもループされていません。
2.一時停止と再開を実装する
でAnimator
2 つのパラメータを作成しTrigger
、2 つのアニメーションを接続します。
アニメーションの一時停止と再開の間の関係:
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. 再起動レベルの実装
再起動レベルのメソッドを次のように追加しますGameManager.cs
。
using UnityEngine.SceneManagement;
// 重启关卡
public void RestartLevel()
{
SceneManager.LoadScene(SceneManager.GetActiveScene().buildIndex);
}
各再起動ボタンで呼び出されます。
実行結果:
11. カメラフォロー
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();
}
}
}
最終的なデモ効果: