概要:类吸血鬼实战(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);
}
}