说一下我的思路:
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来管理生成游戏场景的砖块数量啊 地图长度啊 怪物数量 道具数量种类 等等, 每次游戏通关就重新加载此场景 ,更改一下参数即可
----------------------
本人也是新手,如有建议或者代码不足之处可以指出,有可取之处一定虚心接受