基于Unity3D的经典版炸弹人

说一下我的思路:

1.首先是地图的生成:

        

public void InitMap()
    {
        for (int i = 0; i < length; i += stoneLength)
        {
            for (int j = 0; j < width; j += stoneLength)
            {
                if ((i % 100 == 0 && j % 100 == 0) || i == 0 || j == 0 || i == length - stoneLength || j == width - stoneLength)
                {
                    GameObject obj = Instantiate(Stone, transform);
                    obj.transform.localPosition = new Vector3(i, j);
                    mapDic.Add(new Vector2(i, j),obj);
                    continue;
                }
            }
        }
        StartCoroutine(InitWall());
    }

在这里面我先生成了固定位置的石头,玩过炸弹人的可能知道,砖块和怪物的位置是随机的,或者说是相对随机的,不会出现在角色的附近,所以需要先生成固定的石头,等石头生成完了再生成砖块,其次是怪物,下图:


红线圈出来的位置是不生成砖块和怪物的,下面代码中可以看出来

IEnumerator InitWall()
    {
        for (int i = 0; i < wallNum; i++)
        {
            int x = Random.Range(1, length / stoneLength) * stoneLength;
            int y = Random.Range(1, width / stoneLength) * stoneLength;
            //当mapHash包含此位置 或者 在左上角的位置 则重置 (左上角需要留给玩家,否则没法移动)
            while (mapDic.ContainsKey(new Vector2(x, y)) || (x == stoneLength && y == width - 2 * stoneLength) || (x == stoneLength && y == width - 3 * stoneLength) || (x == 2 * stoneLength && y == width - 2 * stoneLength))
            {
                x = Random.Range(1, length / stoneLength) * stoneLength;
                y = Random.Range(1, width / stoneLength) * stoneLength;
                yield return null;
            }
            //生成道具 在墙前面生成  并且与墙的位置一致  会被墙挡着
            if (i < List_Prop.Count)
            {
                GameObject prop = Instantiate(List_Prop[i],transform);
                prop.transform.localPosition = new Vector3(x, y);
            }
            GameObject obj = Instantiate(wall, transform);
            obj.transform.localPosition = new Vector3(x, y);
            mapDic.Add(new Vector2(x, y),obj);
        }
        StartCoroutine(InitEnemy());
    }

因为要按顺序执行,所以我写了两个协程,一个生成砖块,一个生成怪物,不然没法在一个协程结束之后再调用另一个,上面的代码还有一个生成道具,我们下面再说。到这一步,地图的生成已经完成了

2.怪物的移动

通过我玩了两个小时的炸弹超人发现。。。它的怪物移动有个很奇怪的规律。。。

有时候会按照一个路线一直转悠,有时候会瞎转弯。。

所以我按照自己的思路写了一个怪物的移动

初始怪物会朝一个方向一直走,如果走到不能走的地方会寻找新的能走的方向,再一直走下去,周转往复

    IEnumerator Move()
    {
        //存储可移动的方向列表
        List<Vector2> dirList = Map.Instance.FindDirection(transform.localPosition);
        if (dirList.Count != 0)
        {
            //先随机一个方向
            dir = dirList[Random.Range(0, dirList.Count)];
            //怪物转向找到的方向
            transform.rotation = Quaternion.Euler(0, dir.x == -1 ? 180 : 0, dir.y == 1 ? 0 : -90);
        }

        while (true)
        {
            //如果不能移动
            if (!Map.Instance.IsCanMove(transform.position, dir))
            {
                dir = Vector2.zero;
                float x = ((int)transform.localPosition.x).ChangePos();
                float y = ((int)transform.localPosition.y).ChangePos();

                transform.localPosition = new Vector2(x, y);
                //重新寻找可移动的方向
                dirList = Map.Instance.FindDirection(transform.localPosition);
                if (dirList.Count != 0)
                    dir = dirList[Random.Range(0, dirList.Count)];
                if (dir != Vector2.zero)
                    transform.rotation = Quaternion.Euler(0, dir.x == -1 ? 180 : 0, dir.y == 1 ? 0 : -90);
            }
            transform.localPosition += (Vector3)(dir * speed * Time.deltaTime);
            yield return null;
        }
    }

这个是怪物移动的代码,由于怪物不是像贪吃蛇那样一个单位一个单位移动的,而是平滑移动,我们判断 可移动方向是根据HashSet里面存的Vector2来判断是否可移动,所以会有这样一个问题,比如怪物移动到(49.33,49.56)检测到前面有碰撞物不能移动,需要重新找方向 ,在HashSet里面找方向然后对比判断是否可移动,但是HaseSet里面存的此怪物的上方位置为(100,50),而怪物找到的上方是(99.33,99.56) 两个值不想等就会误判为上方可移动,所以有了以下代码来强制转换位置

public static int ChangePos(this int num)
    {
        // 192   202  152  145 
        if (num < 10)
            return 0;
        if (num >= 100)
        {
            int hundred = num / 100;
            int ten = num / 10 - hundred * 10;
            if (ten >= 9)
            {
                hundred++;
                return hundred * 100;
            }
            if (ten <= 1)
            {
                return hundred * 100;
            }
            ten = 5;
            return hundred * 100 + ten * 10;
        }
        if (num < 70) return 50;
        else return 100;
    }
此代码后面还用来生成炸弹的时候放在砖块的正上方或者正下方,而不是夹在两块砖之间



Map.Instance.FindDirection(transform.localPosition); //寻找可移动方向 这段不难理解

public List<Vector2> FindDirection(Vector2 pos)
    {
        List<Vector2> list = new List<Vector2>();
        Vector2 left = new Vector2(pos.x - stoneLength, pos.y);
        Vector2 right = new Vector2(pos.x + stoneLength, pos.y);
        Vector2 up = new Vector2(pos.x, pos.y + stoneLength);
        Vector2 down = new Vector2(pos.x, pos.y - stoneLength);
        list.Add(left);
        list.Add(right);
        list.Add(up);
        list.Add(down);
        for (int i = 0; i < list.Count; i++)
        {
            if (mapHash.Contain(list[i]))
            {
                list.RemoveAt(i);
                i--;
                continue;
            }
            list[i] = (list[i] - pos).normalized;
        }
        if (list.Count == 0) return new List<Vector2>();
        return list;
    }

!Map.Instance.IsCanMove(transform.position, dir) ; //判断是否可移动

public bool IsCanMove(Vector2 pos,Vector2 dir)
    {
        if (dir == Vector2.zero) return false;
        Debug.DrawLine(pos, pos + dir * 2.3f, Color.red);
        RaycastHit2D hit = Physics2D.Raycast(pos, dir, 2.4f,~(1<<9));
        if (hit.collider != null)
        {
            return false;
        }
        return true;  
    }

本来判断是否可移动也想用预存的HashSet来判断,后来发现还是平滑移动的问题,可能会跳过准确的类似(50,50)的值,所以采用了射线检测

这样怪物的移动也完成了

如果想要怪物看到人就向人的方向移动,我这里提供一个思路:

    可以从怪物向人的方向发射一条射线,具体射线长度视项目而定,如果检测到的第一个碰撞是人物,则向人物移动

3.炸弹的爆炸和道具的投放

人物移动很简单,还是使用物理碰撞

道具的生成,我是把道具存到一个List里面,在生成砖块的时候把道具先生成,然后在道具上面生成砖块,这样就会造成一个你炸弹炸了砖块之后出现道具的假象。。这个代码在上面生成地图的代码上面,我加了备注可以看到,因为是随机生成的砖块,所以道具的位置也是随机的

接下来是炸弹爆炸后的火花,我上面说过炸弹也需要固定位置,类似人物在(49.33,49.56)的位置,按下K键生成的炸弹则在(50,50)的位置,此时你就可以判断当前位置的哪个位置有砖块石块,使用的是上面怪物移动的判定代码,如果有砖块石块,则更改爆炸火花的长度

还有一个是人物生成炸弹后就不能再穿过炸弹了,我是这样解决的  炸弹预制体的触发器勾上,在OnTriggerExit2D里面判断如果人物离开则isTrigger取消勾选


至此一个炸弹人也就完成了,可以创建一个GameSceneCtrl来管理生成游戏场景的砖块数量啊 地图长度啊 怪物数量 道具数量种类 等等, 每次游戏通关就重新加载此场景 ,更改一下参数即可


----------------------

本人也是新手,如有建议或者代码不足之处可以指出,有可取之处一定虚心接受



猜你喜欢

转载自blog.csdn.net/qq_33413868/article/details/79937041
今日推荐