花1.png

类吸血鬼实战(2):敌人组件、子弹组件制作

概要:类吸血鬼实战(2):敌人组件、子弹组件制作

第六课:制作敌人

实现目标

  • 加入精灵图
  • 添加动画
  • 增加刚体
  • 增加碰撞
  • 敌人向人物移动
  • 超出屏幕进行重置敌人位置

Enemy脚本

using System.Collections;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;

public class Enemy : MonoBehaviour
{
    public float speed;
    public Rigidbody2D target;

    bool isLive = true;

    Rigidbody2D rigidbody;
    SpriteRenderer spriteRenderer;

    void Awake()
    {
        rigidbody = GetComponent<Rigidbody2D>();
        spriteRenderer = GetComponent<SpriteRenderer>();
    }
    void FixedUpdate()
    {
        if (!isLive) return;
        Vector2 dirVec = target.position - rigidbody.position;
        Vector2 nextVec = dirVec.normalized * speed * Time.fixedDeltaTime;
        rigidbody.MovePosition(rigidbody.position + nextVec);
        rigidbody.velocity = Vector2.zero;
    }
    // Update is called once per frame
    void Update()
    {

    }
    void LateUpdate()
    {
        if (!isLive) return;
        spriteRenderer.flipX = target.position.x < rigidbody.position.x;
    }
}

Reposition脚本

void OnTriggerExit2D(Collider2D collider)
{
    /*...*/
    switch (transform.tag)
    {
        case "Ground":
            if (diffx > diffy)
            {
                transform.Translate(Vector3.right * dirX * 40);
            }
            else if (diffx < diffy)
            {
                transform.Translate(Vector3.up * dirY * 40);
            }
            break;
        case "Enemy":
            if (coll.enabled)
            {
                transform.Translate(playerDir * 20 + new Vector3(Random.Range(-3f, 3f), Random.Range(-3f, 3f)));
            }
            break;
    }
}

第七课(上):敌人Prefabs

增加敌人prefab

增加PoolManager

修改GameManager

/*...*/
public PoolManager pool;

怪物产生逻辑Spawner

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Spawner : MonoBehaviour
{
    public Transform[] spawnPoint;
    float timer = 0f;
    void Awake()
    {
        spawnPoint = GetComponentsInChildren<Transform>();
    }
    // Update is called once per frame
    void Update()
    {
        timer += Time.deltaTime;
        if (timer > 0.2f)
        {
            Spawn();
            timer = 0f;
        }
    }
    void Spawn()
    {
        GameObject enemy = GameManager.instance.pool.Get(Random.Range(0, 2));
        enemy.transform.position = spawnPoint[Random.Range(1, spawnPoint.Length)].position;
    }
}

PoolManager脚本

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class PoolManager : MonoBehaviour
{
    public GameObject[] prefabs;
    List<GameObject>[] pools;
    void Awake()
    {
        pools = new List<GameObject>[prefabs.Length];
        for (int index = 0; index < pools.Length; index++)
        {
            pools[index] = new List<GameObject>();
        }
    }
    public GameObject Get(int index)
    {
        GameObject select = null;
        foreach (GameObject item in pools[index])
        {
            if (!item.activeSelf)
            {
                select = item;
                select.SetActive(true);
                break;
            }
        }
        if (!select)
        {
            select = Instantiate(prefabs[index], transform);
            pools[index].Add(select);
        }

        return select;
    }
}

第七课(下):敌人等级划分

修改GameManager

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class GameManager : MonoBehaviour
{
    /*...*/
    public float gameTime;
    public float maxGameTime = 2 * 10f;
    /*...*/
    void Update()
    {
        gameTime += Time.deltaTime;
        if (gameTime > maxGameTime)
        {
            gameTime = maxGameTime;
        }
    }
}

修改Spawner敌人生成逻辑

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Spawner : MonoBehaviour
{
    public Transform[] spawnPoint;
    float timer = 0f;
    int level;
    void Awake()
    {
        spawnPoint = GetComponentsInChildren<Transform>();
    }
    // Update is called once per frame
    void Update()
    {
        timer += Time.deltaTime;
        level = Mathf.FloorToInt(GameManager.instance.gameTime / 10f);
        if (timer > (level == 0 ? 0.5f : 0.2f))
        {
            Spawn();
            timer = 0f;
        }
    }
    void Spawn()
    {
        GameObject enemy = GameManager.instance.pool.Get(level);
        enemy.transform.position = spawnPoint[Random.Range(1, spawnPoint.Length)].position;
    }
}

Spawner增加SpawData管理每一关卡的数据

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Spawner : MonoBehaviour
{
    /*...*/
    public SpawData[] spawData;
    float timer = 0f;
    int level;
    /*...*/
    void Update()
    {
        timer += Time.deltaTime;
        level = Mathf.Min(Mathf.FloorToInt(GameManager.instance.gameTime / 10f), spawData.Length - 1);
        if (timer > spawData[level].spawnTime)
        {
            Spawn();
            timer = 0f;
        }
    }
    void Spawn()
    {
        GameObject enemy = GameManager.instance.pool.Get(0);
        enemy.transform.position = spawnPoint[Random.Range(1, spawnPoint.Length)].position;
        enemy.GetComponent<Enemy>().Init(spawData[level]);
    }
}
[System.Serializable]
public class SpawData
{
    public int spriteType;
    public float spawnTime;
    public int health;
    public float speed;

}

统一管理怪物的动画状态机

using System.Collections;
using System.Collections.Generic;
using Unity.VisualScripting;
using UnityEditor;
using UnityEngine;

public class Enemy : MonoBehaviour
{
    /*...*/
    public RuntimeAnimatorController[] animatorControllers;
    /*...*/
    public void Init(SpawData spawData)
    {
        animator.runtimeAnimatorController = animatorControllers[spawData.spriteType];
    }
}

Enemy读取SpawData作为怪物的当前关卡的属性

using System.Collections;
using System.Collections.Generic;
using Unity.VisualScripting;
using UnityEditor;
using UnityEngine;

public class Enemy : MonoBehaviour
{
    /*...*/
    public float speed;
    public float health;
    public float maxHealth
    /*...*/
    public void Init(SpawData spawData)
    {
        /*...*/
        speed = spawData.speed;
        maxHealth = spawData.health;
        health = spawData.health;
    }
}

第八课(上):近战子弹制作

增加子弹预制件

增加bullet脚本

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Bullet : MonoBehaviour
{
    public float damage;
    public int per;
    public void Init(float damage, int per)
    {
        this.damage = damage;
        this.per = per;
    }
}

敌人增加碰撞事件修改Enemy

using System.Collections;
using System.Collections.Generic;
using Unity.VisualScripting;
using UnityEditor;
using UnityEngine;

public class Enemy : MonoBehaviour
{
   /*...*/
    void OnTriggerEnter2D(Collider2D collider)
    {
        if (!collider.CompareTag("Bullet")) return;
        health -= collider.GetComponent<Bullet>().damage;
        if (health > 0)
        {

        }
        else
        {
            Dead();
        }
    }
    void Dead()
    {
        gameObject.SetActive(false);
    }
}

Weapon组件,处理子弹升级、多个子弹旋转位置

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Weapon : MonoBehaviour
{
    // Start is called before the first frame update
    public int id;
    public int prefabId;
    public float damage;
    public int count;
    public float speed;
    void Start()
    {
        Init();
    }
    void Update()
    {
        switch (id)
        {
            case 0:
                transform.Rotate(Vector3.forward * speed * Time.deltaTime);
                break;
            default:
                break;
        }

        if (Input.GetButtonDown("Jump"))
        {
            LevelUp(20, 5);
        }
    }
    public void LevelUp(float damage, int count)
    {
        this.damage = damage;
        this.count += count;
        if (id == 0)
        {
            Batch();
        }
    }
    public void Init()
    {
        switch (id)
        {
            case 0:
                speed = -150;
                Batch();
                break;
            default:
                break;
        }
    }
    void Batch()
    {
        for (int index = 0; index < count; index++)
        {
            Transform bullet;
            if (index < transform.childCount)
            {
                bullet = transform.GetChild(index);
            }
            else
            {
                bullet = GameManager.instance.pool.Get(prefabId).transform;
                bullet.parent = transform;
            }

            bullet.localPosition = Vector3.zero;
            bullet.localRotation = Quaternion.identity;

            Vector3 rotVec = Vector3.forward * 360 * index / count;
            bullet.Rotate(rotVec);
            bullet.Translate(bullet.up * 1.5f, Space.World);
            bullet.GetComponent<Bullet>().Init(damage, -1);
        }
    }
}

第八课(下):近战子弹制作

设置怪物layer

Physics2D.CircleCastAll扫描范围内最近怪物

创建Sanner脚本实现

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Scanner : MonoBehaviour
{
    public float scanRange;
    public LayerMask targetLayer;
    public RaycastHit2D[] targets;
    public Transform nearestTarget;
    void FixedUpdate()
    {
        targets = Physics2D.CircleCastAll(transform.position, scanRange, Vector2.zero, 0, targetLayer);
        nearestTarget = GetNearest();
    }
    Transform GetNearest()
    {
        Transform result = null;
        float diff = 100;

        foreach (RaycastHit2D target in targets)
        {
            Vector3 myPos = transform.position;
            Vector3 targetPos = target.transform.position;
            float curDiff = Vector3.Distance(myPos, targetPos);
            if (curDiff < diff)
            {
                diff = curDiff;
                result = target.transform;
            }
        }

        return result;
    }
}

Player增加获取Scanner对象

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.InputSystem;
public class Player : MonoBehaviour
{
    /*...*/
    public Scanner scanner;
    void Start()
    {
        /*...*/
        scanner = GetComponent<Scanner>();
    }
    /*...*/
}

weapon获取player对象内的scanner,拿到发射对象,增加发射子弹逻辑

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Weapon : MonoBehaviour
{
    /*...*/
    float timer;
    Player player;
    void Awake()
    {
        player = GetComponentInParent<Player>();
    }
    /*...*/
    void Update()
    {
        switch (id)
        {
            case 0:
                transform.Rotate(Vector3.forward * speed * Time.deltaTime);
                break;
            default:
                timer += Time.deltaTime;
                if (timer > speed)
                {
                    timer = 0f;
                    Fire();
                }
                break;
        }
        /*...*/
    }
    /*...*/
    public void Init()
    {
        switch (id)
        {
            case 0:
                speed = -150;
                Batch();
                break;
            default:
                speed = 0.3f;
                break;
        }
    }
    /*...*/
    void Fire()
    {
        if (!player.scanner.nearestTarget) return;
        Transform bullet = GameManager.instance.pool.Get(prefabId).transform;
        bullet.position = transform.position;
    }
}

Bullet增加子弹移动

using System.Collections;
using System.Collections.Generic;
using Unity.VisualScripting;
using UnityEngine;

public class Bullet : MonoBehaviour
{
    public float damage;
    public int per;
    Rigidbody2D rigidbody2D;
    void Awake()
    {
        rigidbody2D = GetComponent<Rigidbody2D>();
    }
    public void Init(float damage, int per, Vector3 dir)
    {
        this.damage = damage;
        this.per = per;
        if (per > -1)
        {
            rigidbody2D.velocity = dir * 15f;
        }
    }
    void OnTriggerEnter2D(Collider2D collider2D)
    {
        if (!collider2D.CompareTag("Enemy") || per == -1) return;
        per--;
        if (per == -1)
        {
            rigidbody2D.velocity = Vector2.zero;
            gameObject.SetActive(false);
        }
    }
}

Weapon脚本内Fire函数创建子弹并移动

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Weapon : MonoBehaviour
{
    /*...*/
    void Fire()
    {
        if (!player.scanner.nearestTarget) return;

        Vector3 targetPos = player.scanner.nearestTarget.position;
        Vector3 dir = targetPos - transform.position;
        dir = dir.normalized;

        Transform bullet = GameManager.instance.pool.Get(prefabId).transform;
        bullet.position = transform.position;
        bullet.rotation = Quaternion.FromToRotation(Vector3.up, dir);
        bullet.GetComponent<Bullet>().Init(damage, count, dir);
    }
}

Created By @Seeyou | 稀有博客