接着上一节:

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微信。

[Untiy]贪吃蛇大作战(五)——游戏主界面的更多相关文章

  1. Unity3D游戏贪吃蛇大作战源码休闲益智手机小游戏完整项目

    <贪吃蛇大作战>一款休闲竞技游戏,不仅比拼手速,更考验玩家的策略. 视频演示: http://player.youku.com/player.php/sid/XMzc5ODA2Njg1Ng ...

  2. 贪吃蛇大作战canvas实现(手机触屏操作)--地图逻辑

    //html部分 <!DOCTYPE html><html><head lang="en"> <meta charset="UT ...

  3. Expo大作战(五)--expo中app.json 文件的配置信息

    简要:本系列文章讲会对expo进行全面的介绍,本人从2017年6月份接触expo以来,对expo的研究断断续续,一路走来将近10个月,废话不多说,接下来你看到内容,讲全部来与官网 我猜去全部机翻+个人 ...

  4. 用cocos2d-html5做的消除类游戏《英雄爱消除》(3)——游戏主界面

    游戏主界面,同时也是主程序,包括sprite的生成加入以及游戏状态的控制. 下面同样贴下源码再讲解; /** * Power by html5中文网(html5china.com) * author: ...

  5. 刺猬大作战(游戏引擎用Free Pascal写成,GUI用C++写成,使用SDL和Qt4)

    游戏特性[编辑] 游戏引擎用Free Pascal写成,GUI用C++写成,使用SDL和Qt4[2]. 0.9.12开始支持实时动态缩放游戏画面. 个性化[编辑] 刺猬大作战有着高度定制性 游戏模式: ...

  6. [知了堂学习笔记]_用JS制作《飞机大作战》游戏_第3讲(玩家发射子弹)

    一.公布上一讲中玩家飞机上.下.右移动实现的代码: /*=========================键盘按下事件 keycode为得到键盘相应键对应的数字==================== ...

  7. 用JS制作《飞机大作战》游戏_第3讲(玩家发射子弹)-陈远波

    一.公布上一讲中玩家飞机上.下.右移动实现的代码: /*=========================键盘按下事件 keycode为得到键盘相应键对应的数字==================== ...

  8. [知了堂学习笔记]_用JS制作《飞机大作战》游戏_第2讲(四大界面之间的跳转与玩家飞机的移动)

    一.通过点击按钮事件,实现四大界面之间的跳转: (一)跳转的思路: 1.打开软件,只显示登录界面(隐藏游戏界面.暂停界面.玩家死亡界面) 2.点击微信登录(QQ登录)跳转到游戏界面,隐藏登录界面 3. ...

  9. 用JS制作《飞机大作战》游戏_第2讲(四大界面之间的跳转与玩家飞机的移动)-陈远波

    一.通过点击按钮事件,实现四大界面之间的跳转: (一)跳转的思路: 1.打开软件,只显示登录界面(隐藏游戏界面.暂停界面.玩家死亡界面) 2.点击微信登录(QQ登录)跳转到游戏界面,隐藏登录界面 3. ...

  10. [知了堂学习笔记]_用JS制作《飞机大作战》游戏_第1讲(素材查找和界面框架搭建)

    一.查找素材: 二.分析游戏界面框架: 登录界面.游戏界面.暂停游戏界面.玩家死亡后弹出界面:并对应的界面包含什么元素: 三.分别搭建以上四个界面: 1.登录界面与游戏界面框架(隐藏游戏界面,四个界面 ...

随机推荐

  1. Map中定义的方法:

    添加.删除.修改操作: Object put(Object key,Object value):将指定key-value添加到(或修改)当前map对象中void putAll(Map m):将m中的所 ...

  2. uoj221【NOI2016】循环之美

    前面部分比较简单,就是无脑化式子,简单点讲好了. 首先肯定在\((x,y)=1\)时才考虑这个分数,要求纯循环的话,不妨猜猜结论,就是y必须和K互质.所以答案是\(\sum_{i=1}^n \sum_ ...

  3. webscraper 无代码爬虫

    官网:https://www.webscraper.io/web-scraper-first-time-install webscraper 简介 Web Scraper 是一款免费的,适用于普通用户 ...

  4. 纯css爱心代码-最近超级火的打火机与公主裙中的爱心代码(简易版)

    theme: cyanosis 最近打火机与公主裙中的爱心代码超级火,看着特别心动,让俺用css来写个简易版!!! 先看效果: 代码拆解: 主要是分为3大部分 分子颗粒 爱心 动画 代码实现: 分子颗 ...

  5. java-代码编写规范

    命名 变量/方法:小驼峰. mBtnHelloWorld 控件 mBtnTest: 按键 mTvTest:文本

  6. MYSQL ---mysql 数据导入与导出

    1.使用SQLyog导出MySQL中的数据库 打开SQLyog → 右击你想要保存的数据库 → 选择"Backup/Export"→ 选择"Backup Database ...

  7. Java8新特性:函数式编程

    1. 概述 函数式编程学习目的: 能够看懂公司里的代码 大数据量下处理集合效率更高 代码可读性高 消灭嵌套地狱 函数式编程思想: 面向对象思想需要关注用什么对象完成什么事情.而函数式编程思想就类似于我 ...

  8. js高级之对象高级部分

    基于尚硅谷的尚硅谷JavaScript高级教程提供笔记撰写,加入一些个人理解 github源码 博客下载 对象的创建模式 Object构造函数模式 套路: 先创建空Object对象, 再动态添加属性/ ...

  9. Go语言核心36讲53

    你好,我是郝林. 在2019年的春节来临之际,我恰好也更新完了专栏所有的配图和思考题答案.希望这些可以帮助到你,在新的一年中,祝你新年快乐,Go语言学习之路更加顺利. 基础概念篇 1. Go语言在多个 ...

  10. 【云原生 · Kubernetes】Kubernetes运维

    (1)Node的隔离与恢复 在硬件升级.硬件维护等情况下,需要将某些Node隔离.使用kubectl cordon <node_name>命令可禁止Pod调度到该节点上,在其上运行的Pod ...