使用Unity3D引擎制作塔防类游戏(二)

时隔这么久,终于写完了关于兵营的大部分功能,写完的同时,还要感慨一下,从头做一个游戏真的好难啊,到现在也只写完一个一级的兵营,问题也不少,写出来分享一下,目前的代码不是很高深,要是有更好的写法,也请不吝赐教。

番外:兵营的一些操作

上一篇已经介绍了如何建造一座防御塔,选择兵营并建造完成后,关于防御塔还有一些额外操作,比如防御塔升级,兵营的集结点设置,卖出后建造新的防御塔之类,这里只讲一下集结点设置(因为只写完了这个),剩下的后续会补充。

(一)设置集结点

使用Unity3D引擎制作塔防类游戏(二)_第1张图片
点击集结点的标志后,出现一面跟着鼠标移动的旗帜,移出攻击范围时,旗帜变为一个×号,即无效集结点。关于判断鼠标是否在有效部署范围之内,我用的是碰撞,代码如下

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);
                }
            }
        }
    }

上面的代码就是使得旗帜跟随鼠标移动,但是由于旗杆位于图片的两侧
使用Unity3D引擎制作塔防类游戏(二)_第2张图片
且鼠标始终位于图片右下角,则旗帜的实际位置是鼠标的位置左移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;
            }
        }

兵营的内容大致就是这些,还有一些代码需要完善,功能也不全,任重而道远啊,还是得继续,期待全部完成之后的效果。

你可能感兴趣的:(Unity3D,unity3d)