接着上一节:

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. 使用dotnet-monitor sidecar模式 dump docker运行的dotnet程序.

    前情概要 随着容器和云技术的发展, 大量的应用运行在云上的容器中, 它们的好处是毋庸置疑的, 例如极大的提高了我们的研发部署速度, 快速的扩缩容等等, 但是也存在一些小小的问题, 例如难以调试. 基于 ...

  2. 成熟企业级开源监控解决方案Zabbix6.2关键功能实战-上

    @ 目录 概述 定义 监控作用 使用理解 监控对象和指标 架构组成 常用监控软件分析 版本选型 俗语 安装 部署方式 部署 zabbix-agent 概述 定义 Zabbix 官网地址 https:/ ...

  3. FastApi学习

    vscode配置 插件 code runner在 setting.json中关于python的修改为,因为我使用了虚拟环境,得让vscode找到python的路径 "code-runner. ...

  4. 【Zulip】邮件系统配置

    通过docker-compose(docker-zulip)部署Zulip实例时需要配置邮件系统 SETTING_ZULIP_ADMINISTRATOR: '...@qq.com' SETTING_E ...

  5. [CS61A] Lecture 5&6&7. Environments & Design & Functions Examples & Homework 2: Higher Order Functions

    [CS61A] Lecture 5&6&7. Environments & Design & Functions Examples & Homework 2: ...

  6. 2022春每日一题:Day 37

    题目:[USACO14FEB]Auto-complete S 字典树套路题,字典树优化剪枝,加个cnt标记即可 代码: #include <cstdio> #include <cst ...

  7. Java开发环境安装与配置(干货详细教程)

    Java开发环境安装与配置(干货详细教程)  对于文章中出现的任何错误请大家批评指出,会及时做出修改!  安装JDK JDK是 Java 语言的软件开发工具包要学习 链接Java中国官网https:/ ...

  8. 聊聊如何让办公网络直连Kubernetes集群PodIP/ClusterIP/Service DNS等

    想象一下,如果您日常使用的研发测试Kubernetes集群,能够有以下效果: 在办公网络下直接访问Pod IP 在办公网络下直接访问Service Cluster IP 在办公网络下直接访问集群内部域 ...

  9. Azure DevOps Server 用户组加入 Azure AD Domain Service 管理用户

    一,引言 今天我们继续讲解 Azure DevOps Server 的内容,对于管理用户组除了在 Azure DevOps Server 服务器上添加管理员方式外,还有没有其他方式,Azure Dev ...

  10. js 获取相同name元素的属性值

    如图有8-12月5个文本域,id为plan8-12,name同为plan. 现在要获取name同为plan的所有属性值: 方法一: var allplan =document.getElementsB ...