(この記事の使用環境: Unity5.4.2f2 Personal、Windows10


Unity公式のSurvival Shooter Tutorialを離れる前に、使われているスクリプトについてメモしておきます。
けっこう再利用できそうなものもあるので。

スクリプトの一覧はこちら。
Stracture

以下、それぞれ簡単に説明しておきます。

1. Camera関連

1-1. CameraFollow.cs
プレイヤーを追従するカメラ。
Vector3.Lerpを使ってスムースフォローしています。

using UnityEngine;
using System.Collections;

public class CameraFollow : MonoBehaviour {

    public Transform target;
    public float smoothing = 5f;

    Vector3 offset;

    private void Start ()
    {
        offset = transform.position - target.position;
    }

    private void FixedUpdate ()
    {
        Vector3 targetCamPos = target.position + offset;
        transform.position = Vector3.Lerp (transform.position, targetCamPos, smoothing * Time.deltaTime);
    }
}

2. Enemy関連

2-1. EnemyAttack.cs
EnemyがPlayerを攻撃します。キモの部分はこんな感じ。

    void OnTriggerEnter (Collider other)
    {
        if (other.gameObject == player)
        {
            playerInRange = true;
        }
    }


    void OnTriggerExit (Collider other)
    {
        if (other.gameObject == player)
        {
            playerInRange = false;
        }
    }


    void Update ()
    {
        timer += Time.deltaTime;

        if (timer >= timeBetweenAttacks && playerInRange && enemyHealth.currentHealth > 0)
        {
            Attack ();
        }

        if (playerHealth.currentHealth <= 0)
        {
            anim.SetTrigger ("PlayerDead");
        }
    }


    void Attack ()
    {
        timer = 0f;

        if (playerHealth.currentHealth > 0)
        {
            playerHealth.TakeDamage (attackDamage);
        }
    }
}


2-2. EnemyHealth.cs

Enemyがダメージを受けて死に、沈んで消えます。

ダメージを受けて死ぬ部分はこんな感じ。
死んだらコライダーをIsTriggerにしているのがポイントでしょうか。

    public void TakeDamage (int amount, Vector3 hitPoint)
    {
        if(isDead)
            return;

        enemyAudio.Play ();

        currentHealth -= amount;
            
        hitParticles.transform.position = hitPoint;
        hitParticles.Play();

        if(currentHealth <= 0)
        {
            Death ();
        }
    }


    void Death ()
    {
        isDead = true;

        capsuleCollider.isTrigger = true;

        anim.SetTrigger ("Dead");

        enemyAudio.clip = deathClip;
        enemyAudio.Play ();
    }

沈んで消える部分はこんな感じ。
アニメーションに加えて、Update()内で沈めています。

    void Update ()
    {
        if(isSinking)
        {
            transform.Translate (-Vector3.up * sinkSpeed * Time.deltaTime);
        }
    }


    public void StartSinking ()
    {
        GetComponent<NavMeshAgent> ().enabled = false;
        GetComponent<Rigidbody> ().isKinematic = true;
        isSinking = true;
        ScoreManager.score += scoreValue;
        Destroy (gameObject, 2f);
    }

2-3. EnemyMovement.cs
自分(Enemy)も相手(Player)も生きている間はNavMeshAgentのSetDestinationでPlayerを追いかけます。

using UnityEngine;
using System.Collections;

public class EnemyMovement : MonoBehaviour
{
    Transform player;
    PlayerHealth playerHealth;
    EnemyHealth enemyHealth;
    NavMeshAgent nav;


    void Awake ()
    {
        player = GameObject.FindGameObjectWithTag ("Player").transform;
        playerHealth = player.GetComponent<PlayerHealth> ();
        enemyHealth = GetComponent<EnemyHealth> ();
        nav = GetComponent<NavMeshAgent> ();
    }


    void Update ()
    {
        if (enemyHealth.currentHealth > 0 && playerHealth.currentHealth > 0)
        {
            nav.SetDestination (player.position);
        }
        else
        {
            nav.enabled = false;
        }
    }
}


3. Managers

3-1. EnemyManager
Enemyを発生させます。
スクリプトはこんな感じ。InvokeRepeatingでEnemyを無限沸きさせています。
spawnPointは配列にして、複数の箇所を設定できるようにしてあります。

using UnityEngine;

public class EnemyManager : MonoBehaviour
{
    public PlayerHealth playerHealth;
    public GameObject enemy;
    public float spawnTime = 3f;
    public Transform[] spawnPoints;


    void Start ()
    {
        InvokeRepeating ("Spawn", spawnTime, spawnTime);
    }


    void Spawn ()
    {
        if(playerHealth.currentHealth <= 0f)
        {
            return;
        }

        int spawnPointIndex = Random.Range (0, spawnPoints.Length);

        Instantiate (enemy, spawnPoints[spawnPointIndex].position, spawnPoints[spawnPointIndex].rotation);
    }
}

Enemyの指定をpublicでアウトレット接続にし、Enemyの種類のぶんだけEnemyManagerスクリプトをアタッチしているのがポイントでしょうか。この方法はいいですね。
EnemyManager_Inspector

3-2. GameOverManager
ゲームオーバー画面の呼び出しと、自動リスタート。
ゲームオーバー画面はAnimatorからアニメーションを呼び出しています。

using UnityEngine;
using UnityEngine.SceneManagement;

public class GameOverManager : MonoBehaviour
{
    public PlayerHealth playerHealth;
    public float restartDelay = 5f;

    Animator anim;
    float restartTimer;

    void Awake ()
    {
        anim = GetComponent<Animator> ();
    }

    void Update ()
    {
        if (playerHealth.currentHealth <= 0)
        {
            anim.SetTrigger ("GameOver");

            restartTimer += Time.deltaTime;

            if (restartTimer >= restartDelay)
            {
                SceneManager.LoadScene ("Level 01");
            }
        }
    }
}

CanvasにAnimatorとGameOverManager.csをアタッチしています。
HUDCanvas

Animationの設定。TextのScaleとColorの変更などなど。
GameOverClip

3-3. ScoreManager

スコアの表示。
UI-Textのオブジェクトに直接アタッチしてUpdateで表示更新しています。
ScoreText

scoreはグローバル変数にしちゃうのがラクですね。

using UnityEngine;
using UnityEngine.UI;
using System.Collections;

public class ScoreManager : MonoBehaviour
{
    public static int score;

    Text text;

    void Awake ()
    {
        text = GetComponent <Text> ();
        score = 0;
    }

    void Update ()
    {
        text.text = "Score: " + score;
    }
}


4. Player関連

4-1. PlayerHealth
ダメージを受ける処理と、死ぬ処理と、リスタート。

ダメージを受ける処理と死ぬ処理。
死んだらPlayerに紐づくスクリプトを無効にして動かなくさせているのがポイントですかね。
スクリプトを賢く分割するとこういうことができるんですね。

    public void TakeDamage (int amount)
    {
        damaged = true;

        currentHealth -= amount;

        healthSlider.value = currentHealth;

        playerAudio.Play ();

        if(currentHealth <= 0 && !isDead)
        {
            Death ();
        }
    }


    void Death ()
    {
        isDead = true;

        playerShooting.DisableEffects ();

        anim.SetTrigger ("Die");

        playerAudio.clip = deathClip;
        playerAudio.Play ();

        playerMovement.enabled = false;
        playerShooting.enabled = false;
    }

    public void RestartLevel ()
    {
        SceneManager.LoadScene (0);
    }

ダメージを受けたら画面を赤く染めて、そのあと徐々にもとに戻しています。

    void Update ()
    {
        if(damaged)
        {
            damageImage.color = flashColour;
        }
        else
        {
            damageImage.color = Color.Lerp (damageImage.color, Color.clear, flashSpeed * Time.deltaTime);
        }
        damaged = false;
    }

4-2. PlayerMovement
MoveとTurningとAnimatingの各処理をFixedUpdate内で呼び出しています。
TurnigではQuaternion.LookRotationを使っていますが、その前にY座標を0にして上下に回転しないようにしています。
Animatingではhまたはvの移動があればWalkingのアニメーションをさせるようにしています。

using UnityEngine;

public class PlayerMovement : MonoBehaviour
{
    public float speed = 6f;

    Vector3 movement;
    Animator anim;
    Rigidbody playerRigidbody;
    int floorMask;

    float camRayLength = 100f;

    float h;
    float v;

    private void Awake ()
    {
        floorMask = LayerMask.GetMask ("Floor");
        anim = GetComponent<Animator> ();
        playerRigidbody = GetComponent<Rigidbody> ();
    }

    private void FixedUpdate ()
    {
        h = Input.GetAxisRaw ("Horizontal");
        v = Input.GetAxisRaw ("Vertical");
        Move (h, v);
        Turning ();
        Animating (h, v);
    }

    void Move (float h, float v)
    {
        movement.Set (h, 0f, v);
        movement = movement.normalized * speed * Time.deltaTime;

        playerRigidbody.MovePosition (transform.position + movement);
    }

    void Turning ()
    {
        Ray camRay = Camera.main.ScreenPointToRay (Input.mousePosition);
        RaycastHit floorHit;
        if (Physics.Raycast (camRay, out floorHit, camRayLength))
        {
            Vector3 playerToMouse = floorHit.point - transform.position;
            playerToMouse.y = 0f;

            Quaternion newRotation = Quaternion.LookRotation (playerToMouse);
            playerRigidbody.MoveRotation (newRotation);
        }
    }

    void Animating (float h, float v)
    {
        bool walking = ( h != 0f || v != 0f );
        anim.SetBool ("IsWalking", walking);
    }
}

4-3. PlayerShooting
Rayを使った光線銃の表現。
この方法を使うと、当たった場所にParticleエフェクトを付けることができます。

using UnityEngine;

public class PlayerShooting : MonoBehaviour
{
    public int damagePerShot = 20;
    public float timeBetweenBullets = 0.15f;
    public float range = 100f;


    float timer;
    Ray shootRay;
    RaycastHit shootHit;
    int shootableMask;
    ParticleSystem gunParticles;
    LineRenderer gunLine;
    AudioSource gunAudio;
    Light gunLight;
    float effectsDisplayTime = 0.2f;


    void Awake ()
    {
        shootableMask = LayerMask.GetMask ("Shootable");
        gunParticles = GetComponent<ParticleSystem> ();
        gunLine = GetComponent<LineRenderer> ();
        gunAudio = GetComponent<AudioSource> ();
        gunLight = GetComponent<Light> ();
    }


    void Update ()
    {
        timer += Time.deltaTime;

        if (Input.GetButton ("Fire1") && timer >= timeBetweenBullets && Time.timeScale != 0)
        {
            Shoot ();
        }

        if (timer >= timeBetweenBullets * effectsDisplayTime)
        {
            DisableEffects ();
        }
    }

    public void DisableEffects ()
    {
        gunLine.enabled = false;
        gunLight.enabled = false;
    }

    void Shoot ()
    {
        timer = 0f;

        gunAudio.Play ();

        gunLight.enabled = true;

        gunParticles.Stop ();
        gunParticles.Play ();

        gunLine.enabled = true;
        gunLine.SetPosition (0, transform.position);

        shootRay.origin = transform.position;
        shootRay.direction = transform.forward;

        if (Physics.Raycast (shootRay, out shootHit, range, shootableMask))
        {
            EnemyHealth enemyHealth = shootHit.collider.GetComponent<EnemyHealth> ();
            if (enemyHealth != null)
            {
                enemyHealth.TakeDamage (damagePerShot, shootHit.point);
            }
            gunLine.SetPosition (1, shootHit.point);
        }
        else
        {
            gunLine.SetPosition (1, shootRay.origin + shootRay.direction * range);
        }
    }
}

けっこうたくさん学べました。

今回は以上です。