时隔这么久,终于写完了关于兵营的大部分功能,写完的同时,还要感慨一下,从头做一个游戏真的好难啊,到现在也只写完一个一级的兵营,问题也不少,写出来分享一下,目前的代码不是很高深,要是有更好的写法,也请不吝赐教。
上一篇已经介绍了如何建造一座防御塔,选择兵营并建造完成后,关于防御塔还有一些额外操作,比如防御塔升级,兵营的集结点设置,卖出后建造新的防御塔之类,这里只讲一下集结点设置(因为只写完了这个),剩下的后续会补充。
点击集结点的标志后,出现一面跟着鼠标移动的旗帜,移出攻击范围时,旗帜变为一个×号,即无效集结点。关于判断鼠标是否在有效部署范围之内,我用的是碰撞,代码如下
private void OnTriggerEnter2D(Collider2D collision)
{
if (collision.name == "Flag")
{
if (FlagStateManager.GetFlagState() != FlagState.Normal)
{
FlagStateManager.SetFlagState(FlagState.Normal);
flag.GetComponent<Image>().sprite = normalFlagSprite;
}
}
}
private void OnTriggerExit2D(Collider2D collision)
{
if (collision.name == "Flag")
{
if (FlagStateManager.GetFlagState() != FlagState.OutOfRange)
{
FlagStateManager.SetFlagState(FlagState.OutOfRange);
flag.GetComponent<Image>().sprite = outOfRangeSprite;
}
}
}
在这里,旗帜的状态判断不是必须的,不过我为了更清晰的分辨旗帜各个状态下操作加上去了。
void Update()
{
//设置集结点,士兵向集结点移动
if (flag.activeSelf)
{
//旗帜跟随鼠标移动
flagRectTransform.anchoredPosition = new Vector3(Input.mousePosition.x - halfFlagSizeDelta.x -
flag.transform.parent.GetComponent<RectTransform>().anchoredPosition.x,
Input.mousePosition.y + halfFlagSizeDelta.y - flag.transform.parent.GetComponent<RectTransform>().anchoredPosition.y);
//集结点在部署范围内
if (FlagStateManager.GetFlagState() == FlagState.Normal)
{
//鼠标左击确定集结点
if (Input.GetMouseButton(0))
{
flag.SetActive(false);
flagPosition = new Vector2(flagRectTransform.anchoredPosition.x + halfFlagSizeDelta.x, flagRectTransform.anchoredPosition.y - halfFlagSizeDelta.y);
defendRange.GetComponent<RectTransform>().anchoredPosition = flagPosition;
if (!defendRange.activeSelf)
{
defendRange.SetActive(true);
}
//设置士兵集结点
soliderCommander.GetComponent<SoliderCommander>().SetSoliderRallyPosition(flagPosition);
}
}
}
}
上面的代码就是使得旗帜跟随鼠标移动,但是由于旗杆位于图片的两侧
且鼠标始终位于图片右下角,则旗帜的实际位置是鼠标的位置左移1/2的旗帜的宽度,上移1/2的旗帜高度。
关于原作的效果,士兵移动时,保持队形不变,且士兵重生后、巡逻范围没有怪物时也自动移向集结点。我是用向量实现的,士兵小队维持的队形是不变的,那么可以先找出队形的中心,为了简便一些,选取了第一个士兵x轴位置,士兵1和士兵2y轴坐标的中点,那么3个士兵到队形中心的向量就计算出来了,且一直不变,那么设置的旗帜位置,其实就是这个中点的位置,该点加上之前的向量就是设置了新的集结点后的每个士兵的位置。
private void Start()
{
solider1Controller = soliders[0].GetComponent<SoliderController>();
solider2Controller = soliders[1].GetComponent<SoliderController>();
solider3Controller = soliders[2].GetComponent<SoliderController>();
//设置士兵待机点到旗帜中心的向量
Vector2 solider1IdlePoint = solider1Controller.idlePoint;
Vector2 solider2IdlePoint = solider2Controller.idlePoint;
Vector2 solider3IdlePoint = solider3Controller.idlePoint;
Vector2 soliderTeamCenter = new Vector2(solider1IdlePoint.x, (solider1IdlePoint.y + solider2IdlePoint.y) / 2);
flagPosition = soliderTeamCenter;
//以下向量加上鼠标设置的旗帜位置就是每个士兵的集结点
solider1PositionToFlag = solider1IdlePoint - flagPosition;
solider2PositionToFlag = solider2IdlePoint - flagPosition;
solider3PositionToFlag = solider3IdlePoint - flagPosition;
}
鼠标点击之后,判断向量的x轴的值来确定士兵移动的方向,根据移动的长度确定士兵移动到指定位置,到达指定位置后,士兵待机。
public void SetRallyPosition(Vector2 position)
{
timer = 0;
moveToFlagVector = new Vector2(0, 0);
if (rallyPosition != position)
{
rallyPosition = position;
}
//士兵处于待机状态才会受到集结点影响
if (soliderState != SoliderState.Attack)
{
//士兵与集结点的向量
moveToFlagVector = rallyPosition - soliderRectTransform.anchoredPosition;
if (moveToFlagVector.x <= 0)
{
soliderAnimator.SetInteger("SoliderState", 0);
}
else
{
soliderAnimator.SetInteger("SoliderState", 3);
transform.GetChild(0).rotation = new Quaternion(0, 0, 0, 1);
}
soliderState = SoliderState.MoveToFlag;
}
}
void Update()
{
//当士兵处于待机状态
if (soliderState == SoliderState.Idle)
{
//每隔一段时间间隔就转换士兵站立姿势
interval += Time.deltaTime;
if (interval > 1.5f)
{
//左右站立姿势随机选择
int randomNum = Random.Range(0, 2);
if (randomNum == 0)
{
if (soliderAnimator.GetInteger("SoliderState") != 1)
{
soliderAnimator.SetInteger("SoliderState", 1);
}
}
else
{
if (soliderAnimator.GetInteger("SoliderState") != 2)
{
soliderAnimator.SetInteger("SoliderState", 2);
}
}
interval = 0;
}
}
//向集结点移动
else if (soliderState == SoliderState.MoveToFlag)
{
if (timer * moveSpeed < moveToFlagVector.magnitude)
{
soliderRectTransform.anchoredPosition += moveToFlagVector.normalized * moveSpeed * Time.deltaTime;
timer += Time.deltaTime;
}
else
{
timer = 0;
if (soliderAnimator.GetInteger("SoliderState") == 0)
{
soliderAnimator.SetInteger("SoliderState", 1);
}
else if (soliderAnimator.GetInteger("SoliderState") == 3)
{
soliderAnimator.SetInteger("SoliderState", 2);
}
soliderState = SoliderState.Idle;
}
}
关于士兵寻找怪物攻击,我添加了一个关于soliderCommander的脚本,挂载到一个碰撞器上,该脚本下的所有士兵只攻击碰撞器检测到的怪物,而关于分配战斗,我只写了一个最简单的实现,每个士兵随机分配阻击对象。
private void Update()
{
for (int i = 0; i < soliders.Count; i++)
{
if (soliders[i] == null)
{
soliders.Remove(soliders[i]);
solidersNumHasChanged = true;
}
}
//当士兵和怪物的数量发生变化,更新战斗队列
if (solidersNumHasChanged || monstersNumHasChanged)
{
//当没有士兵或者怪物时,无需进行战斗匹配
if (soliders.Count == 0 || monsters.Count == 0)
{
return;
}
else
{
for (int i = 0; i < soliders.Count; i++)
{
SoliderController soliderController = soliders[i].GetComponent<SoliderController>();
if (soliderController.isReady)
{
if (soliderController.GetMonsterAttacked() == null)
{
int monsterID = Random.Range(0, monsters.Count);
soliderController.SetMonsterAttacked(monsters[monsterID]);
}
}
}
}
solidersNumHasChanged = false;
monstersNumHasChanged = false;
}
}
public void SetMonsterAttacked(GameObject monster)
{
if (monster != null)
{
findMonsterPath = new Vector2(0, 0);
monsterAttacked = monster;
//当怪兽被选做阻击对象,怪兽停止移动
monsterController = monsterAttacked.GetComponent<MonsterController>();
monsterRectTransform = monsterAttacked.GetComponent<RectTransform>();
monsterController.SetSoliderAttacked(gameObject);
//怪兽层级比士兵高,锚点位置要转换到基于怪兽层级的位置
soliderPosition = transform.parent.GetComponent<RectTransform>().anchoredPosition + soliderRectTransform.anchoredPosition;
//怪兽从左侧过来
if (monsterRectTransform.anchoredPosition.x <= soliderPosition.x)
{
//士兵左转动画过渡
soliderAnimator.SetInteger("SoliderState", 0);
battlePosition = monsterRectTransform.anchoredPosition + new Vector2(15, 0) - soliderPosition;
}
else
{
//士兵右转动画过渡
transform.GetChild(0).rotation = new Quaternion(0, 0, 0, 1);
soliderAnimator.SetInteger("SoliderState", 3);
battlePosition = monsterRectTransform.anchoredPosition - new Vector2(15, 0) - soliderPosition;
}
soliderState = SoliderState.FindMonster;
}
}
寻找怪物的过程用的也是向量,计算当前士兵和分配的怪物进行战斗的位置之间的向量,我的偏移大概是X轴15左右。
else if (soliderState == SoliderState.FindMonster)
{
if (timer != 0)
{
timer = 0;
}
if (monsterAttacked != null)
{
//士兵向左移动
if (soliderAnimator.GetInteger("SoliderState") == 0)
{
if (battlePosition.magnitude > findMonsterPath.magnitude)
{
soliderRectTransform.anchoredPosition += battlePosition.normalized * 2;
findMonsterPath += battlePosition.normalized * 2;
}
else
{
//达到后攻击
soliderAnimator.SetInteger("SoliderState", 4);
soliderState = SoliderState.Attack;
}
}
else if (soliderAnimator.GetInteger("SoliderState") == 3)
{
if (battlePosition.magnitude > findMonsterPath.magnitude)
{
soliderRectTransform.anchoredPosition += battlePosition.normalized * 2;
findMonsterPath += battlePosition.normalized * 2;
}
else
{
soliderAnimator.SetInteger("SoliderState", 5);
soliderState = SoliderState.Attack;
}
}
}
//攻击目标丢失,返回集结点
else
{
battlePosition = new Vector2(0, 0);
findMonsterPath = new Vector2(0, 0);
moveToFlagVector = rallyPosition - soliderRectTransform.anchoredPosition;
if (moveToFlagVector.x <= 0)
{
soliderAnimator.SetInteger("SoliderState", 0);
}
else
{
soliderAnimator.SetInteger("SoliderState", 3);
transform.GetChild(0).rotation = new Quaternion(0, 0, 0, 1);
}
soliderState = SoliderState.MoveToFlag;
}
}
兵营的内容大致就是这些,还有一些代码需要完善,功能也不全,任重而道远啊,还是得继续,期待全部完成之后的效果。