[Untiy]贪吃蛇大作战(五)——游戏主界面
接着上一节:
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]贪吃蛇大作战(五)——游戏主界面的更多相关文章
- Unity3D游戏贪吃蛇大作战源码休闲益智手机小游戏完整项目
<贪吃蛇大作战>一款休闲竞技游戏,不仅比拼手速,更考验玩家的策略. 视频演示: http://player.youku.com/player.php/sid/XMzc5ODA2Njg1Ng ...
- 贪吃蛇大作战canvas实现(手机触屏操作)--地图逻辑
//html部分 <!DOCTYPE html><html><head lang="en"> <meta charset="UT ...
- Expo大作战(五)--expo中app.json 文件的配置信息
简要:本系列文章讲会对expo进行全面的介绍,本人从2017年6月份接触expo以来,对expo的研究断断续续,一路走来将近10个月,废话不多说,接下来你看到内容,讲全部来与官网 我猜去全部机翻+个人 ...
- 用cocos2d-html5做的消除类游戏《英雄爱消除》(3)——游戏主界面
游戏主界面,同时也是主程序,包括sprite的生成加入以及游戏状态的控制. 下面同样贴下源码再讲解; /** * Power by html5中文网(html5china.com) * author: ...
- 刺猬大作战(游戏引擎用Free Pascal写成,GUI用C++写成,使用SDL和Qt4)
游戏特性[编辑] 游戏引擎用Free Pascal写成,GUI用C++写成,使用SDL和Qt4[2]. 0.9.12开始支持实时动态缩放游戏画面. 个性化[编辑] 刺猬大作战有着高度定制性 游戏模式: ...
- [知了堂学习笔记]_用JS制作《飞机大作战》游戏_第3讲(玩家发射子弹)
一.公布上一讲中玩家飞机上.下.右移动实现的代码: /*=========================键盘按下事件 keycode为得到键盘相应键对应的数字==================== ...
- 用JS制作《飞机大作战》游戏_第3讲(玩家发射子弹)-陈远波
一.公布上一讲中玩家飞机上.下.右移动实现的代码: /*=========================键盘按下事件 keycode为得到键盘相应键对应的数字==================== ...
- [知了堂学习笔记]_用JS制作《飞机大作战》游戏_第2讲(四大界面之间的跳转与玩家飞机的移动)
一.通过点击按钮事件,实现四大界面之间的跳转: (一)跳转的思路: 1.打开软件,只显示登录界面(隐藏游戏界面.暂停界面.玩家死亡界面) 2.点击微信登录(QQ登录)跳转到游戏界面,隐藏登录界面 3. ...
- 用JS制作《飞机大作战》游戏_第2讲(四大界面之间的跳转与玩家飞机的移动)-陈远波
一.通过点击按钮事件,实现四大界面之间的跳转: (一)跳转的思路: 1.打开软件,只显示登录界面(隐藏游戏界面.暂停界面.玩家死亡界面) 2.点击微信登录(QQ登录)跳转到游戏界面,隐藏登录界面 3. ...
- [知了堂学习笔记]_用JS制作《飞机大作战》游戏_第1讲(素材查找和界面框架搭建)
一.查找素材: 二.分析游戏界面框架: 登录界面.游戏界面.暂停游戏界面.玩家死亡后弹出界面:并对应的界面包含什么元素: 三.分别搭建以上四个界面: 1.登录界面与游戏界面框架(隐藏游戏界面,四个界面 ...
随机推荐
- 微服务开发框架-----Apache Dubbo
文章目录 一.简介 二.概念与架构 一.简介 Apache Dubbo 是一款微服务开发框架,提供了RPC通信与微服务治理两大关键能力.使用Dubbo开发的微服务,将具备相互之间的远程发现与通信能力, ...
- 齐博x1频道的二次开发入门讲解
要进行频道的二次开发,首先我们要先了解一下频道的目录结构,如下图最基本的几个目录admin 后台文件存放目录index 前台文件存放目录member 会员中心存放目录model 数据表模型目录trai ...
- 实现etcd服务注册与发现
转载自:实现etcd服务注册与发现 0.1.目录结构 . ├── api │ └── main.go ├── common │ └── common.go ├── docker-compose ...
- go-zero docker-compose 搭建课件服务(二):编写courseware rpc服务
0.转载 go-zero docker-compose 搭建课件服务(二):编写courseware rpc服务 0.1源码地址 https://github.com/liuyuede123/go-z ...
- 知识图谱顶会论文(ACL-2022) PKGC:预训练模型是否有利于KGC?可靠的评估和合理的方法
PKGC:预训练模型是否有利于KGC?可靠的评估和合理的方法 论文地址:Do Pre-trained Models Benefit Knowledge Graph Completion? A Reli ...
- [Android开发学iOS系列] Auto Layout
[Android开发学iOS系列] Auto Layout 内容: 介绍什么是Auto Layout. 基本使用方法 在代码中写约束的方法 Auto Layout的原理 尺寸和优先级 Auto Lay ...
- Day16异常1
package com.exception.demo01;public class demo01 { public static void main(String[] args) { try{new ...
- ES 客户端 RestHighLevelClient Connection reset by peer 亲测有效 2022-11-05
导读 最新公司ES集群老出现连接关闭,进而导致查询|写入ES时报错,报错日志显示如下 [2m2022-10-23 14:13:10.088[0;39m - [31mERROR[0;39m - [35m ...
- 重学c#系列——枚举[二十三]
前言 该系列继续更新,枚举介绍. 正文 首先呢,枚举是值类型,这个没什么好说的. enum ConnectionState { DisConnected, Connecting, Connected, ...
- Day19:属性封装详解
属性封装详解 封装即隐藏一些数据,让外部不能直接访问,而是需要通过一些方法才能设置或者访问. 首先我们来看未封装的属性被外部调用的情况 public class Students{//创建一个学生类 ...