using System.Collections;
using System.Collections.Generic;
using UnityEngine;
/**
* 游戏管理器
* 职能
* 游戏操作引导、关卡管理、创建战斗
*
*/
public class GameMgr : MonoBehaviour
{
//内部属性
//--------------------------------------------------------------------------
//内部方法
//--------------------------------------------------------------------------
/** 单例模式
*
*/
public static GameMgr instanse;
/** 战斗管理器
*
*/
[SerializeField] private BattleManager battleManager;
/** 全局配置
* 比如 关卡配置
*
*/
[SerializeField] private GlobleConfs globleConfs;
/** 本地存储记录
*
*/
[SerializeField] private GlobleLocalData globleLocalData;
[SerializeField] private bool battling = false;
private void Awake()
{
instanse = this;
// 初始化配置等
Init();
//TODO:初始化战斗,后面改成开始按钮触发
OnBattle();
}
private void Update()
{
OnUpdate(Time.deltaTime);
}
/** 初始化,包括全局配置
*
*/
void Init()
{
// 关卡
globleConfs = new GlobleConfs();
globleConfs.Load();
// 本地缓存数据
globleLocalData = new GlobleLocalData();
globleLocalData = globleLocalData.GetLocal();
}
/** 选择关卡,发起战斗
*
*/
void OnBattle()
{
// 初始化战斗配置等
battleManager = FindObjectOfType<BattleManager>();
battleManager.InitBattle(globleConfs, globleLocalData);
battling = true;
}
/** 选择关卡,发起战斗
*
*/
void OnUpdate(float deltaTime)
{
if (battling == false) return;
battleManager.OnUpdate(deltaTime);
}
//公开接口
//--------------------------------------------------------------------------
/** 暂时所有游戏对象、或者部分游戏对象
*
*/
public void Pause()
{
// 暂时所有游戏对象
Time.timeScale = 0;
// 遍历数组控制部分对象停止运动
}
/** 游戏胜利
*
*/
public void Success()
{
battling = false;
}
/* 是否战斗中
*
*/
public bool Battling()
{
return battling;
}
public BattleManager BattleMgr()
{
return battleManager;
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
/**
* 战斗管理器
* 职能 : 仅负责战斗逻辑
* (开始、结束、暂停、加速、广告逻辑)
* 核心玩法 :
* 一局游戏大致3分钟,有三波敌人.
* 一波普通攻击,
* 一波大波攻击,
* 最后一波boss攻击.
* 敌人没被打死, 移动到屏幕外会自动循环,再次从出生点开始移动, 直到被打死.
* 一波敌人全部被消灭,敌人才会开始第二波进攻.
*
**/
[System.Serializable]
public class BattleManager : MonoBehaviour
{
//内部属性
//--------------------------------------------------------------------------
// TODO:以后做成读取配置表
public Transform[] enemyBirthTrans; // 敌人出生点
public Transform[] skillBirthTrans; // 技能出生点
public Transform leftTop; // 左上角位置
public Transform rightBottom; // 右下角位置
/** 记录当前第几波
*
*/
private int wave = 1;
/** 切换到下一波之前的准备时间,可以进行一些弹字等
*
*/
private bool switching = false;
private float switchDelta = 0f;
private float switchTime = 5f;
private LevelConfig currLevelConf;
private Coroutine enemyCorotine;
// 本波敌人总数
[SerializeField] private int totalNum;
//外部部属性
//--------------------------------------------------------------------------
public BattleConfs battleConfs;
public BattleDatas battleDatas;
//内部方法
//--------------------------------------------------------------------------
/** 敌人出生逻辑
*
*/
private IEnumerator InvokeEnemy(int curentWave)
{
Debug.LogError("curentWave; " + curentWave);
int num = 0;
string id = "";
switch (curentWave)
{
case 1:
id = currLevelConf.enemy1Id;
num = currLevelConf.GetInt(currLevelConf.enemy1Num);
break;
case 2:
id = currLevelConf.enemy2Id;
num = currLevelConf.GetInt(currLevelConf.enemy2Num);
break;
case 3:
id = currLevelConf.enemy3Id;
num = currLevelConf.GetInt(currLevelConf.enemy3Num);
break;
default:
break;
}
totalNum = num;
yield return new WaitForSeconds(1f);
while (num > 0)
{
//battleDatas.CreateEnemy(id, new Vector3(0, 4f, 0), Quaternion.identity);
int index = Random.Range(0, enemyBirthTrans.Length);
battleDatas.CreateEnemy(id, enemyBirthTrans[index].position, Quaternion.identity);
Debug.LogError("num; " + num);
yield return new WaitForSeconds(2f);
num--;
}
}
/** 停止出生敌人
*
*/
private void StopInvokeEnemy()
{
if (enemyCorotine != null)
{
StopCoroutine("InvokeEnemy");
enemyCorotine = null;
//Debug.LogError("StopCoroutine");
}
}
/** 敌人产生
*
*/
private void OnWaveStart(int currentWave)
{
StopInvokeEnemy();
//创建每一波敌人
enemyCorotine = StartCoroutine("InvokeEnemy",currentWave);
}
/** 玩家胜利
*
*/
private void OnSucces()
{
StopInvokeEnemy();
battleDatas.ClearBattleWave();
GameMgr.instanse.Success();
}
//公开接口
//--------------------------------------------------------------------------
// 初始化战斗
public void InitBattle(GlobleConfs globleConfs, GlobleLocalData globleLocalData)
{
/** 加载战斗配置
*
*/
battleConfs = new BattleConfs();
battleConfs.Load();
/** 初始化战斗数据
*
*/
battleDatas = new BattleDatas();
battleDatas.Reset();
battleDatas.SetData(globleConfs, globleLocalData, battleConfs);
currLevelConf = globleConfs.GetConfById<LevelConfig>(globleLocalData.currentLevel.ToString(), globleConfs.GetLevelConfs());
OnStart();
}
/** 开始游戏
*
*/
public void OnStart()
{
//创建角色和敌人
battleDatas.OnStart();
OnWaveStart(wave);
battleDatas.OnWaveStart(wave);
battleDatas.CreateRole(currLevelConf.playerId, Vector3.zero, Quaternion.identity);
}
/** 每帧更新
* GameMgr 驱动此心跳
*
*/
public void OnUpdate(float deltaTime)
{
if (GameMgr.instanse.Battling() == false) return;
if(battleDatas.EnemyAllDeath(totalNum))
{
if(switching == false)
{
if(wave == 3)
{
Debug.LogError("success!");
OnSucces();
} else {
switching = true;
wave++;
battleDatas.ClearBattleWave();
}
} else {
if(switchDelta > switchTime)
{
switchDelta = 0f;
switching = false;
OnWaveStart(wave);
battleDatas.OnWaveStart(wave);
} else {
switchDelta += deltaTime;
}
}
}
else
{
battleDatas.OnUpdate(deltaTime);
}
}
//公开接口
//--------------------------------------------------------------------------
/** 收否出界
* 左右下方判断,上方不用判断
*
*/
public bool IsOutSide(Vector3 pos)
{
if (pos.x < leftTop.position.x)
return true;
if (pos.x > rightBottom.position.x)
return true;
if (pos.y < rightBottom.position.y)
return true;
return false;
}
/** 矫正出界点
*
*/
public Vector3 CorrectOutSide(Vector3 pos)
{
return new Vector3(pos.x, leftTop.position.y, 0);
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
/**
* 运行时数据
* 职能: 管理(增、删、改、查操作) 角色、敌人、技能、金币 等游戏对象
*
* 玩家 controller
* 敌人 controller (数组)
* 技能 controller (数组)
* 障碍物 controller (数组)
* 子弹移动ai (数组, 子弹会持有发射源的controller, 攻击目标的transform, 子弹有个碰撞检测脚本)
*
*
*
**/
[System.Serializable]
public class BattleDatas
{
//内部属性
//--------------------------------------------------------------------------
GlobleLocalData globleLocalData;
GlobleConfs globleConfs;
BattleConfs battleConfs;
// 敌人引用
[SerializeField] List<EnemyCtr> enmeyCtrs;
// 子弹引用
[SerializeField] List<BulletCtr> bulletList;
// 玩家引用
[SerializeField] RoleCtr roleCtr;
// 本波死亡敌人总数
[SerializeField] int deathNum;
//内部方法
//--------------------------------------------------------------------------
/** 玩家 (不需要移动ai,仅加载攻击ai)
*
*/
private T CreateRoleCommon<T>(RoleConfig conf, Vector3 pos, Quaternion rot) where T : PlayerCtrBase
{
// 加载模型
var obj = UtilTool.CreateObjByName(conf.prefab, pos, rot);
// 添加武器ai
var weaponConf = battleConfs.GetConfById<WeaponConfig>(conf.weaponAiId, battleConfs.GetWeaponConfs());
var weaponAI = obj.AddComponent(GlobalFunction.GetTypeByClassName(weaponConf.aiName)) as ShootBaseAI;
// 添加controller
var ctr = obj.AddComponent(typeof(T)) as T;
ctr.SetConfig(conf, weaponConf);
ctr.SetWeaponAI(weaponAI);
ctr.IsEnemy = false;
return ctr;
}
/** 敌人 (加载移动ai, 攻击ai)
*
*/
private T CreateEnemyCommon<T>(RoleConfig conf, Vector3 pos, Quaternion rot) where T : PlayerCtrBase
{
// 加载模型
var obj = UtilTool.CreateObjByName(conf.prefab, pos, rot);
// 添加移动ai
var moveConf = battleConfs.GetConfById<MoveAIConfig>(conf.moveAiId, battleConfs.GetMoveAIConfs());
var moveAI = obj.AddComponent(GlobalFunction.GetTypeByClassName(moveConf.aiName)) as BaseAI;
// 添加武器ai
var weaponConf = battleConfs.GetConfById<WeaponConfig>(conf.weaponAiId, battleConfs.GetWeaponConfs());
var weaponAI = obj.AddComponent(GlobalFunction.GetTypeByClassName(weaponConf.aiName)) as ShootBaseAI;
// 添加controller
var ctr = obj.AddComponent(typeof(T)) as T;
ctr.SetConfig(conf, weaponConf);
ctr.SetMoveAI(moveAI);
ctr.SetWeaponAI(weaponAI);
ctr.IsEnemy = true;
return ctr;
}
/** 创建子弹(加载移动ai)
*
*/
private T CreateBulletCommon<T>(BulletConfig conf, Vector3 pos, Quaternion rot, bool isEnemy) where T : BulletCtrBase
{
// 加载模型
var obj = UtilTool.CreateObjByName(conf.prefab, pos, rot);
// 添加移动ai
var moveConf = battleConfs.GetConfById<MoveAIConfig>(conf.moveAiId, battleConfs.GetMoveAIConfs());
var moveAI = obj.AddComponent(GlobalFunction.GetTypeByClassName(moveConf.aiName)) as BaseAI;
// 添加controller
var ctr = obj.AddComponent(typeof(T)) as T;
ctr.SetMoveAI(moveAI);
ctr.SetConfig(conf);
ctr.IsEnemy = isEnemy;
return ctr;
}
/** 回收敌人
*
*/
private void ClearEnemys()
{
for (int i = 0; i < enmeyCtrs.Count; i++)
{
enmeyCtrs[i] = null;
}
enmeyCtrs.Clear();
}
/** 回收子弹
*
*/
private void ClearBullets()
{
for (int i = 0; i < bulletList.Count; i++)
{
var obj = bulletList[i];
if (obj != null)
{
GameObject.Destroy(obj.gameObject);
}
bulletList[i] = null;
}
bulletList.Clear();
}
//公开接口
//--------------------------------------------------------------------------
/** 重置
*
*/
public void Reset()
{
roleCtr = null;
enmeyCtrs = new List<EnemyCtr>();
bulletList = new List<BulletCtr>();
}
/** 初始化
*
*/
public void SetData(GlobleConfs gConfs, GlobleLocalData gLocalData, BattleConfs bConfs)
{
globleLocalData = gLocalData;
globleConfs = gConfs;
battleConfs = bConfs;
}
/** 开始游戏
*
*/
public void OnStart()
{
}
/** 每一波开始
*
*/
public void OnWaveStart(int currentWave)
{
deathNum = 0;
}
/** 每帧更新
* BattleManager 驱动此心跳
*
*/
public void OnUpdate(float deltaTime)
{
roleCtr.OnUpdate(deltaTime);
for (int i = 0; i < enmeyCtrs.Count;i++)
{
enmeyCtrs[i].OnUpdate(deltaTime);
}
for (int i = 0; i < bulletList.Count; i++)
{
bulletList[i].OnUpdate(deltaTime);
}
}
/** 创建角色
*
*/
public RoleCtr CreateRole(string roleId, Vector3 pos, Quaternion rot)
{
var conf = battleConfs.GetConfById<RoleConfig>(roleId, battleConfs.GetRoleConfs()); //currLevelConf.playerId
var ctr = CreateRoleCommon<RoleCtr>(conf, pos, rot);
ctr.OnStart();
roleCtr = ctr;
return ctr;
}
/** 创建敌人
*
*/
public EnemyCtr CreateEnemy(string enemyId, Vector3 pos, Quaternion rot)
{
var conf = battleConfs.GetConfById<RoleConfig>(enemyId, battleConfs.GetRoleConfs());
var ctr = CreateEnemyCommon<EnemyCtr>(conf, pos, rot);
ctr.OnStart();
enmeyCtrs.Add(ctr);
return ctr;
}
/** 创建子弹
*
* isEnemy 是玩家还是敌人发射的
*/
public BulletCtr CreateBullet(string bullectID, Vector3 pos, Quaternion rot, bool isEnemy)
{
var conf = battleConfs.GetConfById<BulletConfig>(bullectID, battleConfs.GetBulletConfs());
var ctr = CreateBulletCommon<BulletCtr>(conf, pos, rot, isEnemy);
ctr.OnStart();
bulletList.Add(ctr);
return ctr;
}
/** 距离参考者最近的敌人
*
*/
public EnemyCtr NearestEnemy(Transform refer)
{
EnemyCtr ctr = null;
float tempDis = float.MaxValue;
float dis;
foreach(var v in enmeyCtrs)
{
dis = Vector3.Distance(refer.position, v.transform.position);
if(tempDis > dis)
{
tempDis = dis;
ctr = v;
}
}
return ctr;
}
/** 距离参考者最近的玩家
*
*/
public RoleCtr NearestPlayer(Transform refer)
{
return roleCtr;
}
/** 掉血
*
*/
public void CutHp(PlayerCtrBase role, int dhp)
{
role.GetData().CutHP(dhp);
}
/** 玩家角色死亡
*
*/
public void RoleDeath(RoleCtr role)
{
if(role == roleCtr)
{
//roleCtr.IsDead = true;
roleCtr = null;
}
}
/** 敌人死亡
*
*/
public void EnemyDeath(EnemyCtr enemy)
{
if(enmeyCtrs.Contains(enemy))
{
//enemy.IsDead = true;
deathNum++;
GameObject.Destroy(enemy.gameObject);
enmeyCtrs.Remove(enemy);
}
}
/** 敌人是否全部消灭
* 用来判断下一波进行何时进行
* 或者比赛胜利
*
*/
public bool EnemyAllDeath(int totalNum)
{
return deathNum == totalNum;
}
/** 清理上一波数据
*
*/
public void ClearBattleWave()
{
ClearEnemys();
ClearBullets();
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class MoveBaseAI : BaseAI
{
// 移动角色 transform
[SerializeField] protected Transform roleTrans;
// 移动速度
[SerializeField] protected float moveSpeed;
// 角色(玩家/敌人) controller
[SerializeField] protected Transform targetTrans;
// 移动方向
[SerializeField] protected Vector3 moveDir;
[SerializeField] protected bool noTarget;
// 战斗管理器
[SerializeField] protected BattleManager battle;
/** 移动朝向
* 分两种:
* 一种是朝着固定方向移动,初始化时给予一个direction,
* 另一种是跟随目标切换移动方向,需要在心跳里面逐帧更新目标.
*
*/
protected virtual Vector3 GetDir()
{
if (this.noTarget)
{
//Debug.LogError("no target");
return this.moveDir;
} else{
var dir = this.targetTrans.position - this.roleTrans.position;
return dir.normalized;
}
}
/** 初始化ai (必要的引用、数据)
*
*
*/
public override void OnStart(params object[] args)
{
if (args == null || args.Length < 4)
{
Debug.LogError("args 必须包含 移动transform 、移动速度、移动方向、目标transform");
return;
}
battle = GameMgr.instanse.BattleMgr();
this.roleTrans = (Transform)args[0];
this.moveSpeed = float.Parse(args[1].ToString());
if (args[2] != null) {
this.noTarget = true;
this.moveDir = (Vector3)args[2];
} else {
this.noTarget = false;
this.targetTrans = (Transform)args[3];
this.moveDir = this.GetDir();
}
base.OnStart(args);
}
/** 每帧更新(外界驱动)
*
*/
public override void OnUpdate(float deltaTime)
{
//Debug.Log("single forward deltaTime : " + deltaTime);
//Debug.Log("single forward isStart : " + this.isStart);
//Debug.Log("single forward roleTrans : " + roleTrans.name);
base.OnUpdate(deltaTime);
if(this.roleTrans == null){
//被消灭
return;
}
if (battle.IsOutSide(this.roleTrans.position))
{
// 出界了,强制矫正
this.roleTrans.position = battle.CorrectOutSide(this.roleTrans.position);
}
this.moveDir = this.GetDir();
this.roleTrans.position += this.moveDir * deltaTime * this.moveSpeed;
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
/** 子弹移动ai(数组, 子弹会持有发射源的controller,攻击目标的transform)
* 子弹有个碰撞检测脚本
*/
public class ShootBaseAI : BaseAI
{
//内部属性
//--------------------------------------------------------------------------
// 发射者 transform
[SerializeField] protected Transform roleTrans;
// 武器配置
[SerializeField] protected WeaponConfig weaponConf;
// 角色(玩家/敌人) controller
[SerializeField] protected PlayerCtrBase roleCtr;
// 攻击频率
[SerializeField] protected float shootInterval;
// 上次攻击时间
[SerializeField] protected float shootDeltaTime;
// 战斗管理器
[SerializeField] protected BattleManager battle;
//内部方法
//--------------------------------------------------------------------------
/** 发射朝向
*
*
*/
protected virtual Vector3 GetDir()
{
return Vector3.zero;
}
/** 发射子弹
* 子弹出生点为发射者的位置
* 子弹的朝向分为三类:
* (1) 默认垂直方向,敌人向下发射 ; 玩家向上发射.
* (2) 锁定一个目标位置,向目标位置发射
* (3) 尾随目标,每帧刷新目标位置.
*
*
*/
protected void Shoot()
{
var pos = roleTrans.position;
//Debug.LogError("GetDir "+Quaternion.Euler(this.GetDir()));
var rot = Quaternion.Euler(this.GetDir());
battle.battleDatas.CreateBullet(weaponConf.bulletID, pos, rot, this.roleCtr.IsEnemy);
}
//公开接口
//--------------------------------------------------------------------------
/** 初始化ai (必要的引用、数据)
* 数组
* 支持多个不同类型的参数
* 比如 var targetTrans = (Transform)args[0];
*
*
*/
public override void OnStart(params object[] args)
{
if (args == null || args.Length < 3)
{
Debug.LogError("args 必须包含 角色transform、武器配置 、角色controller");
return;
}
battle = GameMgr.instanse.BattleMgr();
this.roleCtr = (PlayerCtrBase)args[0];
this.roleTrans = (Transform)args[1];
this.weaponConf = (WeaponConfig)args[2];
this.shootInterval = this.weaponConf.GetFloat(this.weaponConf.shootInterval);
base.OnStart(args);
}
/** 每帧更新
* 角色 controller 驱动心跳
*/
public override void OnUpdate(float deltaTime)
{
base.OnUpdate(deltaTime);
if (this.roleTrans == null)
{
//被消灭
return;
}
if (this.shootDeltaTime > this.shootInterval)
{
this.shootDeltaTime = 0f;
this.Shoot();
} else {
this.shootDeltaTime += deltaTime;
}
}
/** 每帧更新
* 角色 controller 驱动心跳
*
* OnUpdateTimeOnly 与 OnUpdate 互斥 , 同一帧只能执行其中一个.
* 比如玩家不滑动角色,角色会更新武器时间,但是不会攻击(即使攻击时间到了).
*
*/
public void OnUpdateTimeOnly(float deltaTime)
{
base.OnUpdate(deltaTime);
if (this.roleTrans == null)
{
//被消灭
return;
}
this.shootDeltaTime += deltaTime;
}
}