ダークモード
スクリプト完成版一覧
第19回まで終わったときの、すべてのスクリプトの完成形です。 授業の途中で「どこかおかしい!」となったときの見比べ用に使ってください。
この教材のコードについて
この教材のコードは Unity 6(6000.0 LTS)向けに書いています。実際のプロジェクトでの動作検証は順次行っています。
置き場所
すべて Assets/_Game/Scripts/ に置きます。ファイル名とクラス名(public class ○○の○○)は必ず同じにしてください。
| スクリプト | 付ける場所 | 作る回 |
|---|---|---|
| PlayerController | Player | 第3回(第4回で追加) |
| PlayerShooter | Player | 第5回(第9・15・16回で追加) |
| Bullet | PlayerBullet / Laser / Missile プレハブ | 第5回(第8・15回で追加) |
| BackgroundScroller | 背景の隕石など | 第6回 |
| Enemy | 敵プレハブ・ボス | 第7回(第8・9・10・14・17回で追加) |
| EnemySpawner | EnemySpawner | 第7回(第11・13回で追加) |
| PlayerLife | Player | 第8回(第9・11・15・19回で追加) |
| GameManager | GameManager | 第10回(第11・12・18・19回で追加) |
| TitleMenu | タイトルシーンの TitleMenu | 第12回(第19回で追加) |
| EnemyWave | 波のように動く敵 | 第13回 |
| EnemyShooter | 弾を撃つ敵 | 第13回 |
| EnemyBullet | EnemyBullet プレハブ | 第13回 |
| PowerUpCapsule | PowerUpCapsule プレハブ | 第14回 |
| PowerUpManager | Player | 第14回(第15・16回で追加) |
| OptionFollower | Option プレハブ | 第16回 |
| Boss | Boss | 第17回 |
| BossHPBar | Canvas の BossHPBar | 第17回 |
| StageData | (アセットとして作る) | 第18回 |
| StageController | StageController | 第18回 |
| CameraShake | Main Camera | 第19回 |
PlayerController
csharp
using UnityEngine;
using UnityEngine.InputSystem;
// 自機を動かすスクリプト
public class PlayerController : MonoBehaviour
{
public float speed = 6f; // 移動の速さ
// 動ける範囲(第4回)
public float minX = -8f;
public float maxX = 8f;
public float minY = -4.5f;
public float maxY = 4.5f;
// 機体を傾ける演出(第4回)
public Transform bank; // 傾ける子オブジェクト
public float bankAngle = 30f; // 傾ける角度
InputAction moveAction;
void Start()
{
moveAction = InputSystem.actions.FindAction("Move");
}
void Update()
{
// 入力を読む(左右が x、上下が y。-1 から 1 の値)
Vector2 input = moveAction.ReadValue<Vector2>();
// 動かす
Vector3 move = new Vector3(input.x, input.y, 0f);
transform.position += move * speed * Time.deltaTime;
// 画面からはみ出さないようにする
Vector3 pos = transform.position;
pos.x = Mathf.Clamp(pos.x, minX, maxX);
pos.y = Mathf.Clamp(pos.y, minY, maxY);
transform.position = pos;
// 上下に動くときに機体を傾ける
if (bank != null)
{
Quaternion target = Quaternion.Euler(-input.y * bankAngle, 0f, 0f);
bank.localRotation = Quaternion.Lerp(bank.localRotation, target, 10f * Time.deltaTime);
}
}
}PlayerShooter
csharp
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.InputSystem;
// 弾の種類(第15回)
public enum ShotType
{
Normal,
Double,
Laser
}
// 弾を撃つスクリプト
public class PlayerShooter : MonoBehaviour
{
public GameObject bulletPrefab; // ふつうの弾
public Transform muzzle; // 弾が出る場所
public float fireInterval = 0.15f; // 連射の間隔(秒)
public AudioClip shotSound; // 発射音(第9回)
public GameObject laserPrefab; // レーザー(第15回)
public GameObject missilePrefab; // ミサイル(第15回)
public ShotType shotType = ShotType.Normal;
public bool hasMissile = false;
public List<Transform> options = new List<Transform>(); // オプション(第16回)
float timer;
InputAction attackAction;
AudioSource audioSource;
void Start()
{
attackAction = InputSystem.actions.FindAction("Attack");
audioSource = GetComponent<AudioSource>();
}
void Update()
{
timer -= Time.deltaTime;
if (attackAction.IsPressed() && timer <= 0f)
{
Shoot();
timer = fireInterval;
}
}
void Shoot()
{
Fire(muzzle.position);
// オプションからも撃つ
foreach (Transform option in options)
{
Fire(option.position);
}
if (shotSound != null)
{
audioSource.PlayOneShot(shotSound);
}
}
// 指定した場所から、今の装備に合わせて弾を出す
void Fire(Vector3 position)
{
if (shotType == ShotType.Laser)
{
Instantiate(laserPrefab, position, Quaternion.identity);
}
else
{
Instantiate(bulletPrefab, position, Quaternion.identity);
if (shotType == ShotType.Double)
{
// 斜め上(45度)にも撃つ
Instantiate(bulletPrefab, position, Quaternion.Euler(0f, 0f, 45f));
}
}
if (hasMissile)
{
// 斜め下(-45度)にミサイル
Instantiate(missilePrefab, position, Quaternion.Euler(0f, 0f, -45f));
}
}
}Bullet
csharp
using UnityEngine;
// 自機の弾(ふつうの弾・レーザー・ミサイル共通)
public class Bullet : MonoBehaviour
{
public float speed = 15f; // 速さ
public float lifeTime = 2f; // 何秒で消えるか
public int damage = 1; // 敵に与えるダメージ(第8回)
public bool pierce = false; // 敵をつらぬくか(第15回 レーザー用)
void Start()
{
Destroy(gameObject, lifeTime);
}
void Update()
{
// 自分の向いている「右」へ進む(回転させると斜めに飛ぶ)
transform.Translate(Vector3.right * speed * Time.deltaTime);
}
// 何かにぶつかったとき(第8回)
void OnTriggerEnter(Collider other)
{
Enemy enemy = other.GetComponent<Enemy>();
if (enemy != null)
{
enemy.TakeDamage(damage);
if (!pierce)
{
Destroy(gameObject);
}
}
}
}BackgroundScroller
csharp
using UnityEngine;
// 背景の飾りを左へ流し、画面の左に出たら右へ戻す
public class BackgroundScroller : MonoBehaviour
{
public float speed = 1f; // 流れる速さ
public float resetX = -14f; // ここより左へ行ったら
public float startX = 14f; // ここへ戻す
void Update()
{
transform.Translate(Vector3.left * speed * Time.deltaTime, Space.World);
if (transform.position.x < resetX)
{
Vector3 pos = transform.position;
pos.x = startX;
transform.position = pos;
}
}
}Enemy
csharp
using UnityEngine;
// 敵の基本(移動・HP・やられたとき)
public class Enemy : MonoBehaviour
{
public float speed = 3f; // 左へ進む速さ
public int hp = 1; // 体力(第8回)
public GameObject explosionPrefab; // 爆発(第9回)
public AudioClip explosionSound; // 爆発音(第9回)
public int score = 100; // 倒したときの点数(第10回)
public GameObject capsulePrefab; // 落とすカプセル(第14回)
public bool isBoss = false; // ボスかどうか(第17回)
[HideInInspector] public int maxHp; // 最初の体力(第17回)
bool isDead = false;
void Awake()
{
maxHp = hp;
}
void Update()
{
transform.Translate(Vector3.left * speed * Time.deltaTime, Space.World);
// 画面の左に出たら消す
if (transform.position.x < -12f)
{
Destroy(gameObject);
}
}
// ダメージを受ける(第8回)
public void TakeDamage(int damage)
{
if (isDead) return;
hp -= damage;
if (hp <= 0)
{
Die();
}
}
void Die()
{
isDead = true;
GameManager.instance.AddScore(score);
if (explosionPrefab != null)
{
Instantiate(explosionPrefab, transform.position, Quaternion.identity);
}
if (explosionSound != null)
{
AudioSource.PlayClipAtPoint(explosionSound, Camera.main.transform.position);
}
if (capsulePrefab != null)
{
Instantiate(capsulePrefab, transform.position, Quaternion.identity);
}
if (isBoss)
{
CameraShake.instance.Shake(1f, 0.4f);
GameManager.instance.StageClear();
}
Destroy(gameObject);
}
}EnemySpawner
csharp
using System.Collections;
using UnityEngine;
// 敵を一定時間ごとに出す
public class EnemySpawner : MonoBehaviour
{
public GameObject[] enemyPrefabs; // 出す敵(第13回で配列に)
public float interval = 1.5f; // 出す間隔(秒)
public float minY = -4f; // 出す高さの範囲
public float maxY = 4f;
public int formationCount = 1; // 一度に何機つなげて出すか(第13回)
public float formationGap = 0.3f; // つなげて出すときの間隔
void Start()
{
StartCoroutine(SpawnLoop());
}
IEnumerator SpawnLoop()
{
while (true)
{
yield return new WaitForSeconds(interval);
// ゲームが終わっていたら出さない(第11回)
if (!GameManager.instance.isPlaying) continue;
// ランダムに敵の種類と高さを決める
GameObject prefab = enemyPrefabs[Random.Range(0, enemyPrefabs.Length)];
float y = Random.Range(minY, maxY);
for (int i = 0; i < formationCount; i++)
{
Vector3 pos = new Vector3(transform.position.x, y, 0f);
Instantiate(prefab, pos, Quaternion.identity);
yield return new WaitForSeconds(formationGap);
}
}
}
}PlayerLife
csharp
using System.Collections;
using UnityEngine;
// 自機がやられる・復活する
public class PlayerLife : MonoBehaviour
{
public GameObject explosionPrefab; // 爆発(第9回)
public AudioClip explosionSound; // 爆発音(第9回)
public GameObject model; // 点滅させる見た目(第11回)
public float invincibleTime = 2f; // 無敵時間(第11回)
bool isInvincible = false;
void OnTriggerEnter(Collider other)
{
if (isInvincible) return;
if (other.CompareTag("Enemy") || other.CompareTag("EnemyBullet"))
{
if (other.CompareTag("EnemyBullet"))
{
Destroy(other.gameObject);
}
// シールドがあれば、シールドが代わりに消える(第15回)
PowerUpManager powerUp = GetComponent<PowerUpManager>();
if (powerUp != null && powerUp.UseShield())
{
return;
}
Hit();
}
}
void Hit()
{
if (explosionPrefab != null)
{
Instantiate(explosionPrefab, transform.position, Quaternion.identity);
}
if (explosionSound != null)
{
AudioSource.PlayClipAtPoint(explosionSound, Camera.main.transform.position);
}
CameraShake.instance.Shake(0.3f, 0.3f); // 第19回
// パワーアップを元に戻す(第15回)
PowerUpManager powerUp = GetComponent<PowerUpManager>();
if (powerUp != null)
{
powerUp.ResetAll();
}
GameManager.instance.LoseLife();
if (GameManager.instance.lives > 0)
{
StartCoroutine(Respawn());
}
else
{
gameObject.SetActive(false);
}
}
// 少しのあいだ点滅しながら無敵になる
IEnumerator Respawn()
{
isInvincible = true;
transform.position = new Vector3(-6f, 0f, 0f);
float time = 0f;
while (time < invincibleTime)
{
model.SetActive(!model.activeSelf);
yield return new WaitForSeconds(0.1f);
time += 0.1f;
}
model.SetActive(true);
isInvincible = false;
}
}GameManager
csharp
using TMPro;
using UnityEngine;
using UnityEngine.SceneManagement;
// スコア・残機・ゲームの状態を管理する
public class GameManager : MonoBehaviour
{
public static GameManager instance; // どこからでも使えるようにする
public int score = 0;
public TextMeshProUGUI scoreText;
public int lives = 3; // 残機(第11回)
public TextMeshProUGUI livesText;
public GameObject gameOverPanel;
public bool isPlaying = true; // ゲーム中かどうか
public GameObject clearPanel; // クリア画面(第18回)
public TextMeshProUGUI highScoreText; // ハイスコア(第19回)
void Awake()
{
instance = this;
}
void Start()
{
UpdateUI();
}
public void AddScore(int amount)
{
score += amount;
UpdateUI();
}
public void LoseLife()
{
lives--;
UpdateUI();
if (lives <= 0)
{
GameOver();
}
}
void GameOver()
{
isPlaying = false;
SaveHighScore();
gameOverPanel.SetActive(true);
}
public void StageClear()
{
isPlaying = false;
SaveHighScore();
clearPanel.SetActive(true);
}
// ハイスコアを保存する(第19回)
void SaveHighScore()
{
int best = PlayerPrefs.GetInt("HighScore", 0);
if (score > best)
{
PlayerPrefs.SetInt("HighScore", score);
PlayerPrefs.Save();
}
UpdateUI();
}
void UpdateUI()
{
scoreText.text = "SCORE " + score.ToString("D6");
livesText.text = "LIFE " + lives;
if (highScoreText != null)
{
highScoreText.text = "HI " + PlayerPrefs.GetInt("HighScore", 0).ToString("D6");
}
}
// ボタンから呼ぶ
public void Retry()
{
SceneManager.LoadScene(SceneManager.GetActiveScene().name);
}
public void BackToTitle()
{
SceneManager.LoadScene("Title");
}
}TitleMenu
csharp
using TMPro;
using UnityEngine;
using UnityEngine.SceneManagement;
// タイトル画面のボタン
public class TitleMenu : MonoBehaviour
{
public TextMeshProUGUI highScoreText; // 第19回
void Start()
{
if (highScoreText != null)
{
highScoreText.text = "HI-SCORE " + PlayerPrefs.GetInt("HighScore", 0).ToString("D6");
}
}
public void StartGame()
{
SceneManager.LoadScene("Game");
}
public void QuitGame()
{
Application.Quit();
}
}EnemyWave
csharp
using UnityEngine;
// 波のように上下しながら進む(Enemy と一緒に付ける)
public class EnemyWave : MonoBehaviour
{
public float amplitude = 1.5f; // 波の高さ
public float frequency = 1.5f; // 波の細かさ
float startY;
void Start()
{
startY = transform.position.y;
}
void Update()
{
// x の位置で高さを決めるので、つなげて出すと「ヘビ」のような編隊になる
Vector3 pos = transform.position;
pos.y = startY + Mathf.Sin(pos.x * frequency) * amplitude;
transform.position = pos;
}
}EnemyShooter
csharp
using UnityEngine;
// 自機をねらって弾を撃つ(Enemy と一緒に付ける)
public class EnemyShooter : MonoBehaviour
{
public GameObject bulletPrefab; // EnemyBullet プレハブ
public float interval = 2f; // 撃つ間隔(秒)
float timer;
void Start()
{
timer = interval;
}
void Update()
{
timer -= Time.deltaTime;
if (timer <= 0f)
{
Shoot();
timer = interval;
}
}
void Shoot()
{
GameObject player = GameObject.FindWithTag("Player");
if (player == null) return; // 自機がいなければ撃たない
// 自機への向きを角度にする
Vector3 dir = player.transform.position - transform.position;
float angle = Mathf.Atan2(dir.y, dir.x) * Mathf.Rad2Deg;
Instantiate(bulletPrefab, transform.position, Quaternion.Euler(0f, 0f, angle));
}
}EnemyBullet
csharp
using UnityEngine;
// 敵の弾(向いている方向へまっすぐ進む)
public class EnemyBullet : MonoBehaviour
{
public float speed = 5f;
public float lifeTime = 5f;
void Start()
{
Destroy(gameObject, lifeTime);
}
void Update()
{
transform.Translate(Vector3.right * speed * Time.deltaTime);
}
}PowerUpCapsule
csharp
using UnityEngine;
// パワーアップカプセル
public class PowerUpCapsule : MonoBehaviour
{
public float speed = 1.5f;
void Start()
{
Destroy(gameObject, 15f);
}
void Update()
{
transform.Translate(Vector3.left * speed * Time.deltaTime, Space.World);
transform.Rotate(0f, 180f * Time.deltaTime, 0f); // くるくる回す
}
void OnTriggerEnter(Collider other)
{
if (other.CompareTag("Player"))
{
PowerUpManager powerUp = other.GetComponent<PowerUpManager>();
if (powerUp != null)
{
powerUp.AddCapsule();
}
Destroy(gameObject);
}
}
}PowerUpManager
csharp
using UnityEngine;
using UnityEngine.InputSystem;
using UnityEngine.UI;
// グラディウス風パワーアップゲージ
public class PowerUpManager : MonoBehaviour
{
// ゲージのマス(0:SPEED 1:MISSILE 2:DOUBLE 3:LASER 4:OPTION 5:?)
public Image[] gaugeSlots;
public Color normalColor = new Color(0.2f, 0.2f, 0.5f);
public Color selectedColor = Color.yellow;
// パワーアップの中身(第15回)
public PlayerController controller;
public PlayerShooter shooter;
public float speedUpAmount = 1.5f;
public float maxSpeed = 12f;
public GameObject shield;
// オプション(第16回)
public GameObject optionPrefab;
public int maxOptions = 2;
int cursor = -1; // 光っているマス(-1 はどこも光っていない)
float startSpeed;
InputAction powerUpAction;
void Start()
{
powerUpAction = InputSystem.actions.FindAction("PowerUp");
startSpeed = controller.speed;
UpdateGauge();
}
void Update()
{
if (powerUpAction.WasPressedThisFrame() && cursor >= 0)
{
Activate(cursor);
cursor = -1;
UpdateGauge();
}
}
// カプセルを取ったとき:光るマスを1つ右へ
public void AddCapsule()
{
cursor++;
if (cursor >= gaugeSlots.Length)
{
cursor = 0;
}
UpdateGauge();
}
void Activate(int index)
{
switch (index)
{
case 0: // SPEED UP
controller.speed = Mathf.Min(controller.speed + speedUpAmount, maxSpeed);
break;
case 1: // MISSILE
shooter.hasMissile = true;
break;
case 2: // DOUBLE
shooter.shotType = ShotType.Double;
break;
case 3: // LASER
shooter.shotType = ShotType.Laser;
break;
case 4: // OPTION
AddOption();
break;
case 5: // ?(シールド)
shield.SetActive(true);
break;
}
}
// オプションを増やす(第16回)
void AddOption()
{
if (shooter.options.Count >= maxOptions) return;
// 1つ目は自機を、2つ目は1つ目のオプションを追いかける
Transform target = transform;
if (shooter.options.Count > 0)
{
target = shooter.options[shooter.options.Count - 1];
}
GameObject option = Instantiate(optionPrefab, target.position, Quaternion.identity);
option.GetComponent<OptionFollower>().target = target;
shooter.options.Add(option.transform);
}
// シールドがあれば使う。使えたら true を返す
public bool UseShield()
{
if (shield.activeSelf)
{
shield.SetActive(false);
return true;
}
return false;
}
// やられたときに全部元に戻す
public void ResetAll()
{
controller.speed = startSpeed;
shooter.shotType = ShotType.Normal;
shooter.hasMissile = false;
shield.SetActive(false);
foreach (Transform option in shooter.options)
{
Destroy(option.gameObject);
}
shooter.options.Clear();
cursor = -1;
UpdateGauge();
}
void UpdateGauge()
{
for (int i = 0; i < gaugeSlots.Length; i++)
{
if (i == cursor)
{
gaugeSlots[i].color = selectedColor;
}
else
{
gaugeSlots[i].color = normalColor;
}
}
}
}OptionFollower
csharp
using System.Collections.Generic;
using UnityEngine;
// 自機(または前のオプション)が通った道をあとから追いかける
public class OptionFollower : MonoBehaviour
{
public Transform target; // 追いかける相手
public int delay = 12; // どれくらい遅れてついてくるか
Queue<Vector3> history = new Queue<Vector3>();
Vector3 lastTargetPos;
void Start()
{
lastTargetPos = target.position;
}
void LateUpdate()
{
// 相手が動いたときだけ記録する(止まるとオプションも止まる)
if (target.position != lastTargetPos)
{
history.Enqueue(target.position);
lastTargetPos = target.position;
if (history.Count > delay)
{
transform.position = history.Dequeue();
}
}
}
}Boss
csharp
using System.Collections;
using UnityEngine;
// ボスの動きと攻撃(Enemy と一緒に付ける。Enemy の speed は 0 にする)
public class Boss : MonoBehaviour
{
public float enterX = 6f; // ここまで入ってきたら戦闘開始
public float enterSpeed = 2f;
public float moveRange = 3f; // 上下に動く幅
public float moveSpeed = 1f;
public GameObject bulletPrefab; // EnemyBullet プレハブ
public Transform muzzle; // 弾が出る場所
enum State
{
Enter, // 登場中
Fight // 戦闘中
}
State state = State.Enter;
float fightTime = 0f;
void Update()
{
switch (state)
{
case State.Enter:
transform.Translate(Vector3.left * enterSpeed * Time.deltaTime, Space.World);
if (transform.position.x <= enterX)
{
state = State.Fight;
StartCoroutine(AttackLoop());
}
break;
case State.Fight:
fightTime += Time.deltaTime;
Vector3 pos = transform.position;
pos.y = Mathf.Sin(fightTime * moveSpeed) * moveRange;
transform.position = pos;
break;
}
}
// 攻撃パターンをくり返す
IEnumerator AttackLoop()
{
while (true)
{
// パターン1:扇形に5発 × 3回
for (int i = 0; i < 3; i++)
{
FireSpread();
yield return new WaitForSeconds(0.6f);
}
yield return new WaitForSeconds(1f);
// パターン2:自機ねらいの連射
for (int i = 0; i < 8; i++)
{
FireAimed();
yield return new WaitForSeconds(0.15f);
}
yield return new WaitForSeconds(1f);
}
}
void FireSpread()
{
for (int angle = -30; angle <= 30; angle += 15)
{
// 180度 = 左向き
Instantiate(bulletPrefab, muzzle.position, Quaternion.Euler(0f, 0f, 180f + angle));
}
}
void FireAimed()
{
GameObject player = GameObject.FindWithTag("Player");
if (player == null) return;
Vector3 dir = player.transform.position - muzzle.position;
float angle = Mathf.Atan2(dir.y, dir.x) * Mathf.Rad2Deg;
Instantiate(bulletPrefab, muzzle.position, Quaternion.Euler(0f, 0f, angle));
}
}BossHPBar
csharp
using UnityEngine;
// ボスの体力バー(Fill の横の大きさを変える)
public class BossHPBar : MonoBehaviour
{
public Enemy boss;
public RectTransform fill; // Pivot X を 0(左はし)にしておく
void Update()
{
if (boss == null)
{
// ボスが倒されたらバーを消す
gameObject.SetActive(false);
return;
}
float rate = (float)boss.hp / boss.maxHp;
fill.localScale = new Vector3(rate, 1f, 1f);
}
}StageData
csharp
using UnityEngine;
// 「何秒に、どの敵を、どこに、何機」出すかの1行分
[System.Serializable]
public class SpawnInfo
{
public float time; // ステージ開始から何秒後か
public GameObject enemyPrefab;
public float y; // 出す高さ
public int count = 1; // 何機つなげて出すか
public float gap = 0.3f; // つなげるときの間隔
}
// ステージの「敵の出現表」
[CreateAssetMenu(fileName = "StageData", menuName = "Shooting/Stage Data")]
public class StageData : ScriptableObject
{
public SpawnInfo[] spawns; // 時間の早い順に並べる
public float bossTime = 60f; // ボスが出てくる時間
}StageController
csharp
using System.Collections;
using UnityEngine;
// StageData の表どおりに敵を出し、最後にボスを出す
public class StageController : MonoBehaviour
{
public StageData stageData;
public GameObject boss; // シーンに置いて非表示にしておいたボス
public GameObject bossHPBar; // 非表示にしておいた体力バー
public GameObject warningText; // 「WARNING!!」の文字
void Start()
{
StartCoroutine(StageFlow());
}
IEnumerator StageFlow()
{
float time = 0f;
foreach (SpawnInfo info in stageData.spawns)
{
// 次の出現時間まで待つ
yield return new WaitForSeconds(info.time - time);
time = info.time;
StartCoroutine(SpawnGroup(info));
}
// ボスの時間まで待つ
yield return new WaitForSeconds(stageData.bossTime - time);
warningText.SetActive(true);
yield return new WaitForSeconds(3f);
warningText.SetActive(false);
boss.SetActive(true);
bossHPBar.SetActive(true);
}
IEnumerator SpawnGroup(SpawnInfo info)
{
for (int i = 0; i < info.count; i++)
{
if (!GameManager.instance.isPlaying) yield break;
Vector3 pos = new Vector3(transform.position.x, info.y, 0f);
Instantiate(info.enemyPrefab, pos, Quaternion.identity);
yield return new WaitForSeconds(info.gap);
}
}
}CameraShake
csharp
using System.Collections;
using UnityEngine;
// カメラをゆらす
public class CameraShake : MonoBehaviour
{
public static CameraShake instance;
Vector3 startPos;
void Awake()
{
instance = this;
startPos = transform.localPosition;
}
public void Shake(float duration, float strength)
{
StopAllCoroutines();
StartCoroutine(ShakeRoutine(duration, strength));
}
IEnumerator ShakeRoutine(float duration, float strength)
{
float time = 0f;
while (time < duration)
{
Vector2 offset = Random.insideUnitCircle * strength;
transform.localPosition = startPos + new Vector3(offset.x, offset.y, 0f);
time += Time.deltaTime;
yield return null;
}
transform.localPosition = startPos;
}
}