接着上一节:

4、AI蛇的设计

这里AI蛇大部分代码都可以参照主角的代码,我这里的实现其实还可以进行改进。基本原理就是蛇创建之后给蛇一个随机方向的单位向量,AI蛇的蛇头添加一个比蛇头大两三倍大小的碰撞检测盒,效果如图:

当蛇头的碰撞检测盒子与墙壁或者其他蛇的身体碰撞时,给蛇一个反向160-200度的向量,这样AI蛇就不会碰撞到墙壁或者其他蛇的身体上死亡了。代码如下:

using System.Collections;
using System.Collections.Generic;
using UnityEngine; public class AISnakeController : MonoBehaviour { [Tooltip("蛇移动的速度")]
public int speed;
[Tooltip("蛇头")]
public Sprite[] snakeHeads;
[Tooltip("蛇身体")]
public Sprite[] snakeBodys;
[Tooltip("初始身体的数量")]
public int initBodyNum = 4;
[Tooltip("蛇身体对象")]
public GameObject snakeBody;
public Quaternion direction;
//蛇头产生的一些坐标
private List<Vector2> oldPositionList;
//蛇身体移动的步数
private int positionLength = 5;
//生成的蛇身体
public List<GameObject> _bodys;
//皮肤的编号
public int skinNum;
// Use this for initialization
public int addLengthNeedFood = 10;
public int addLengthNeedFoodReset = 10;
[Tooltip("击杀的敌人")]
public int killEnemyNum = 0;
void Start()
{
//Debug.Log("游戏开始了,蛇头的名字是" + "skin" + StaticData.Instance.usingSkinName + "head");
direction = Quaternion.Euler(new Vector3(0,0,Random.Range(0f,360f))); InitHead();
InitBody();
AddAIBody(transform); } // Update is called once per frame
void Update()
{ }
private void FixedUpdate()
{
if (GameController.Instance.isGameOver)
{
return;
}
UpdateRotationAndMove(); }
/// <summary>
/// 更新头部的旋转角度
/// </summary>
private void UpdateRotationAndMove()
{ oldPositionList.Insert(0, transform.position);
Vector3 vec =direction * Vector3.up;
transform.position += vec* speed * Time.deltaTime;
transform.rotation = direction;
FollowHead(); } /// <summary>
/// 初始化头部
/// </summary>
private void InitHead()
{ skinNum = Random.Range(1,5);
var sprite = GetComponent<SpriteRenderer>().sprite;
GetComponent<SpriteRenderer>().sprite = snakeHeads[skinNum - 1];
if (skinNum == 2 || skinNum == 4)
transform.localScale = new Vector3(1f, 1f, 1);
else
transform.localScale = new Vector3(0.5f, 0.5f, 1);
transform.localPosition = new Vector3(0, 0, 0);
GetComponent<SpriteRenderer>().sortingOrder = 1;
var cc=gameObject.AddComponent<CircleCollider2D>();
var rb = gameObject.AddComponent<Rigidbody2D>();
cc.radius = 0.5f;
cc.isTrigger = true;
rb.gravityScale = 0;
//创建蛇身体的存储
oldPositionList = new List<Vector2>();
//一开始有5个蛇身体,每个身体的间隔为positionLength个单元
for (int i = 0; i < 6 * positionLength + 1; i++)
{
oldPositionList.Add(new Vector2(transform.position.x, transform.position.y - 0.07f * (i + 1)));
} }
/// <summary>
/// 初始化身体
/// </summary>
private void InitBody()
{
_bodys = new List<GameObject>();
for (int i = 0; i < initBodyNum; i++)
{
GameObject go = new GameObject("body");
var comp = go.AddComponent<SpriteRenderer>();
comp.sprite = snakeBodys[skinNum - 1];
//因为父类对象缩小了0.5,所以这里要除以2 go.transform.SetParent(snakeBody.transform);
//go.transform.position = new Vector3(transform.position.x,
//go.transform.position=new Vector3(transform.position.x,
//-0.5f * (i + 1),
//0);
// , transform.position.z);
comp.sortingLayerName = "character";
if (skinNum == 2 || skinNum == 4)
go.transform.localScale = new Vector3(1f, 1f, 1f);
else
go.transform.localScale = new Vector3(0.5f, 0.5f, 1f);
go.transform.position = transform.position;
var collider = go.AddComponent<CircleCollider2D>(); go.tag = "Player";
_bodys.Add(go); }
//for (int i = 0; i < _bodys.Count; i++)
//{
// Debug.Log(oldPositionList[(i + 1) * positionLength-1]);
//} }
/// <summary>
/// 增加身体
/// </summary>
private void AddBody()
{
GameObject go = new GameObject("body");
var comp = go.AddComponent<SpriteRenderer>();
comp.sprite = snakeBodys[skinNum - 1];
//因为父类对象缩小了0.5,所以这里要除以2 go.transform.SetParent(snakeBody.transform);
comp.sortingLayerName = "character";
if (skinNum == 2 || skinNum == 4)
go.transform.localScale = new Vector3(1f, 1f, 1f);
else
go.transform.localScale = new Vector3(0.5f, 0.5f, 1f);
go.transform.position = transform.position;
var collider = go.AddComponent<CircleCollider2D>();
go.tag = "Player";
_bodys.Add(go); }
/// <summary>
/// 跟随头部
/// </summary>
private void FollowHead()
{ for (int i = 0; i < _bodys.Count; i++)
{ _bodys[i].transform.position = oldPositionList[(i + 1) * (positionLength)]; } if (oldPositionList.Count > _bodys.Count * positionLength + 40)
{
oldPositionList.RemoveAt(oldPositionList.Count - 1);
} //}
//if ((_bodys.Count+2)*positionLength> oldPositionList.Count) }
/// <summary>
/// 碰撞检测
/// </summary>
/// <param name="collision"></param>
private void OnTriggerEnter2D(Collider2D collision)
{
if (collision.tag == "Food")
{
FoodPoolManager.Instance.changeFoodState(collision.gameObject);
addLengthNeedFood--;
if (addLengthNeedFood <= 0)
{
AddBody();
addLengthNeedFood = addLengthNeedFoodReset;
}
}
else if (collision.tag == "Border")
{
Dead();
}
else if (collision.tag == "BigFood")
{
addLengthNeedFood -= 3;
Destroy(collision.gameObject);
if (addLengthNeedFood <= 0)
{
AddBody();
addLengthNeedFood = addLengthNeedFoodReset;
}
}
//}else if (collision .tag=="AIBody")
//{
// //if (_bodys.Contains(collision.gameObject))
// // return;
// //var vec3 = direction.eulerAngles;
// //vec3.z = Random.Range(vec3.z + 180 - 10, vec3.z+ 180 + 10);
// //direction = Quaternion.Euler(vec3);
//} else if (collision.tag == "Player")
{
if (_bodys.Contains(collision.gameObject))
return;
if (collision.transform.parent.tag == "AI")
{
AISnakeController sc = collision.transform.parent.parent.gameObject.GetComponent<AISnakeController>();
sc.KillEnemy();
}
else
{
SnakeController sc2;
if (collision.transform.parent.name == "SnakeBody")
sc2 = collision.transform.parent.gameObject.GetComponent<SnakeController>();
else
sc2 = collision.gameObject.GetComponent<SnakeController>();
sc2.KillEnemy();
}
Dead(); }
}
/// <summary>
/// 击杀了敌人
/// </summary>
public void KillEnemy()
{
this.killEnemyNum++;
}
public void ResetState()
{
foreach (var i in _bodys)
{
Destroy(i);
}
oldPositionList.Clear();
_bodys.Clear();
//蛇身体移动的步数
positionLength = 5;
//生成的蛇身体
//皮肤的编号
skinNum = Random.Range(1, 5);
// Use this for initialization
addLengthNeedFood = 10;
addLengthNeedFoodReset = 10;
killEnemyNum = 0;
var sprite = GetComponent<SpriteRenderer>().sprite;
GetComponent<SpriteRenderer>().sprite = snakeHeads[skinNum - 1];
if (skinNum == 2 || skinNum == 4)
transform.localScale = new Vector3(1f, 1f, 1f);
else
transform.localScale = new Vector3(0.5f, 0.5f, 1f);
this.transform.localPosition = new Vector3(0, 0, 0);
direction = Quaternion.Euler(new Vector3(0, 0, Random.Range(0f, 360f)));
//一开始有5个蛇身体,每个身体的间隔为positionLength个单元
for (int i = 0; i < 6 * positionLength + 1; i++)
{
oldPositionList.Add(new Vector2(transform.position.x, transform.position.y - 0.07f * (i + 1)));
}
InitBody();
}
public void AddAIBody(Transform parent)
{
var go = new GameObject("AIBody");
go.tag = "AIBody";
go.transform.parent = parent;
if (skinNum == 2 || skinNum == 4)
go.transform.localScale = new Vector3(1f, 1f, 1f);
else
go.transform.localScale = new Vector3(2f, 2f, 1f);
//go.transform.localScale = new Vector3(2f, 2f, 1);
go.transform.localPosition = new Vector3(0f, 0f, 0f); var rb=go.AddComponent<Rigidbody2D>();
rb.gravityScale = 0; var cc = go.AddComponent<CircleCollider2D>();
cc.radius = 1f;
cc.isTrigger = true; go.AddComponent<TriggerEnter>(); }
public void Dead()
{
List<Vector2> pos = new List<Vector2>();
foreach (var i in _bodys)
{
pos.Add(i.transform.position);
}
var gj = GameObject.Find("BigFoodSpawner");
gj.GetComponent<BigFoodSpawner>().CreateBigFood(pos);
ResetState();
}
}

5、分数排行的实现

这里的排行榜我们通过脚本获取到每个蛇身上的分数属性来进行随时调整,这里我们需要使用一个排序算法来按照蛇的长度进行排序,排序之后即可按照顺序将蛇的长度和名字显示在排行榜的UI元素上,代码如下:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI; public class RankListUpdate : MonoBehaviour {
[Tooltip("排行榜的标签")]
public GameObject[] rankList;
[Tooltip("所有的蛇")]
public GameObject[] snakes; // Update is called once per frame
void Update () { }
private void LateUpdate()
{
// Debug.Log(snakes[4].transform.GetChild(0).GetComponent<SnakeController>()._bodys.Count);
Sort();
UpdateList();
}
private void Sort()
{
for (int i=0;i<snakes.Length;i++)
{
for (int j=i+1;j<snakes.Length;j++)
{
//if (snakes[j].name == "Snake")
//{
// Debug.LogError("没错啊,输出了4啊");
//}
//Debug.LogError(i + ":::" + j);
int lengthOne = (snakes[i].name=="Snake"? snakes[i].transform.GetChild(0).GetComponent<SnakeController>()._bodys.Count : snakes[i].transform.GetChild(0).GetComponent<AISnakeController>()._bodys.Count);
int lengthTwo=(snakes[j].name == "Snake" ? snakes[j].transform.GetChild(0).GetComponent<SnakeController>()._bodys.Count : snakes[j].transform.GetChild(0).GetComponent<AISnakeController>()._bodys.Count);
if (lengthOne<lengthTwo)
{
var temp = snakes[i];
snakes[i] = snakes[j];
snakes[j] = temp;
}
}
}
}
private void UpdateList()
{
for (int i=0;i<rankList.Length;i++)
{
int length= (snakes[i].name == "Snake" ? snakes[i].transform.GetChild(0).GetComponent<SnakeController>()._bodys.Count : snakes[i].transform.GetChild(0).GetComponent<AISnakeController>()._bodys.Count);
var name=rankList[i].transform.GetChild(0);
var score= rankList[i].transform.GetChild(1);
name.GetComponent<Text>().text = snakes[i].name;
score.GetComponent<Text>().text = length.ToString();
}
} }

到此,游戏的大部分困难的地方的思路都有了,其他简单的地方有一些unity基础就可以很快实现,希望大家能做出一个更好的贪吃蛇大作战。

有任何思路不顺的地方欢迎给我留言评论或者加我的QQ微信。

最新文章

  1. 从CPU的运行到函数调用做个了解
  2. HNOI2006-公路修建问题(二分答案+并查集)
  3. 初见IOS的UI之:UI控件的属性frame bounds center 和transform
  4. 附加数据库 对于 服务器“00-PC”失败
  5. 异常 java.lang.NumberFormatException: For input string:
  6. listview 点击条目 自动置顶或者自动置底部
  7. android保存照片到相册的一些事
  8. Tabhost中Activity绑定Service
  9. MySQL 存储过程的简单使用
  10. Android源代码下载 “Gerrit下载源代码”
  11. CF #552(div3)F 背包问题
  12. [转]windows BLE编程 net winform 连接蓝牙4.0
  13. StringBuffer的append方法比“+”高效
  14. 使用Postgres,Nginx和Gunicorn将Django配置到服务器上
  15. [HNOI2002] 营业额统计
  16. nano,pico文本编辑器,debian执行crontab -e
  17. python-tkinter学习实例
  18. go post 上传文件的例子
  19. ARKit从入门到精通(8)-ARKit捕捉平地
  20. knowledge 开源知识管理系统

热门文章

  1. uoj349【WC2018】即时战略
  2. UEC 利用代理/委托写一个生命组件
  3. Golang-Gin Response 统一返回restful格式的数据
  4. Python基础部分:12、文件光标移动(补充)
  5. linux如何删除多余网卡
  6. Java-ArrayList常用方法
  7. 第2-3-8章 分片上传和分片合并的接口开发-文件存储服务系统-nginx/fastDFS/minio/阿里云oss/七牛云oss
  8. 4 c++编程-提高篇-STL简介
  9. easui datagrid 行获取后台sql所有数据:支持行chockbox多选,输出选中行任意属性;支持点击表中属性实现跳转;支持分页。
  10. Isaac SDK &amp; Sim 环境