当前位置: 首页 > news >正文

Unity3D 小案例 像素贪吃蛇 02 蛇的觅食

Unity3D 小案例 像素贪吃蛇 第二期 蛇的觅食

像素贪吃蛇

食物生成

在场景中创建一个 2D 正方形,调整颜色,添加 Tag 并修改为 Food。

创建食物

然后拖拽到 Assets 文件夹中变成预制体。

预制体

创建食物管理器 FoodManager.cs,添加单例,可以设置食物生成的坐标范围,提供生成一个食物的方法。

因为 Random.Range 的取值范围是 [min, max),为了取到 max 的值,需要给右边界加一。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class FoodManager : MonoBehaviour
{public static FoodManager instance;public GameObject food;public int borderLeft = -8;public int borderRight = 8;public int borderTop = 4;public int borderBottom = -4;void Awake(){if (instance == null){instance = this;}else{Destroy(gameObject);}}void Start(){// 初始生成一个食物GenerateFood();}/// <summary>/// 生成食物/// </summary>public void GenerateFood(){GameObject obj = Instantiate(food, transform);int x = Random.Range(borderLeft, borderRight + 1);int y = Random.Range(borderBottom, borderTop + 1);obj.transform.position = new Vector3(x, y, 0);}
}

在场景中创建节点,挂上脚本,拖拽引用。

食物管理器

运行游戏,可以看到场景中生成了一个食物。

生成一个食物

吃掉食物

给食物的预制体添加碰撞体,勾选 Is Trigger

添加碰撞体

同样,蛇头也要添加碰撞体,还要再添加一个刚体,Body Type 设置为 Kinematic,不需要受到重力影响。

添加刚体

Snake.cs 中添加碰撞函数,判断碰撞物体的标签是 Food,就销毁食物,生成新的蛇身,并生成下一个食物。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class Snake : MonoBehaviour
{// ...void OnTriggerEnter2D(Collider2D other){if (other.CompareTag("Food")){Destroy(other.gameObject);GenerateBody();FoodManager.instance.GenerateFood();}}
}

此时运行游戏,蛇头可以吃掉食物了。

但是有时候蛇头还未到达食物的位置,食物就被吃掉了,甚至蛇头只是经过食物的附近,食物也消失了。这是因为碰撞体的范围问题,默认的 Size 是 (1, 1),可以稍微调小一些,例如 (0.5, 0.5)

调整碰撞体大小

调整后的效果:

吃掉食物

食物位置

目前场景范围适中,生成的食物都在空地,但是当蛇越来越长的时候,会发现食物生成的位置有可能在蛇的身上。

我们应该让食物始终都在空地生成。

那么,对于一个坐标是否为空地,就需要做一些标记。

网格

目前食物生成的坐标取值范围,在 X 轴是 [-8, 8],在 Y 轴是 [-4, 4]

如果把这些坐标点看成是一个网格,可以按照行列来看。

左上角是 (-8, 4),是第 0 行,第 0 列,索引为 0。

右上角是 (8, 4),是第 0 行,第 16 列,索引为 16。

左下角是 (-8, -4),是第 8 行,第 0 列,索引为 136。

右下角是 (8, -4),是第 8 行,第 16 列,索引为 152。

注意:这里的索引是从第 0 行开始,从左到右递增。行数增加时,索引继续计数。

网格

网格列表

FoodManager.cs 中,添加一个 Vector3 列表,X 和 Y 记录坐标,Z 记录是否空地(0 表示空地,1 表示有物体占用)。

这里总行数是上边界减去下边界,还要加上一个端点,总共 9 行。

总列数是右边界减去左边界,还要加上一个端点,总共 17 列。

根据行列数,依次添加 Vector3 到列表中,Z 默认是 0。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class FoodManager : MonoBehaviour
{// ...public List<Vector3> gridList;public int rowMax = 0;public int colMax = 0;void Start(){rowMax = borderTop - borderBottom + 1;colMax = borderRight - borderLeft + 1;for (int i = 0; i < rowMax; i++){for (int j = 0; j < colMax; j++){gridList.Add(new Vector3(borderLeft + j, borderTop - i, 0));}}}
}

然后提供一个标记网格列表的方法,把传入的坐标转成 int,判断边界,换算行列,计算索引,根据索引从网格列表中取出一个网格点,更新标记。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class FoodManager : MonoBehaviour
{// .../// <summary>/// 标记网格列表/// </summary>/// <param name="pos">坐标位置</param>/// <param name="flag">标记</param>public void MarkGridList(Vector3 pos, bool flag){int x = (int)pos.x;int y = (int)pos.y;// 坐标超出边界if (x < borderLeft || x > borderRight) return;if (y < borderBottom || y > borderTop) return;// 换算行列int row = borderTop - y;int col = x - borderLeft;// 计算索引int index = col + row * colMax;// 索引超出边界if (index < 0 || index > gridList.Count - 1) return;// 取出网格点,标记是否空地Vector3 grid = gridList[index];grid.z = flag ? 1 : 0;// 更新网格点gridList[index] = grid;}
}

标记网格

在游戏开始时,蛇头会占用一个网格,生成的身体也需要标记网格。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class Snake : MonoBehaviour
{void Start(){// 初始生成身体for (int i = 0; i < initBodyCount; i++){GenerateBody();}FoodManager.instance.MarkGridList(transform.position, true);// ...}void GenerateBody(){GameObject obj = Instantiate(body);// ...FoodManager.instance.MarkGridList(obj.transform.position, true);}
}

在蛇的移动过程中,也要动态地标记网格。

蛇头和身体移动后都要标记网格已经被占用,只有在最后一个身体移动前,标记当前网格位置为空地。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class Snake : MonoBehaviour
{void Move(){// ...// 移动前,先标记旧的位置posMarkFirst = transform.position;transform.Translate(direction);// 标记蛇头移动后的网格位置FoodManager.instance.MarkGridList(transform.position, true);// ...for (int i = 0; i < bodyList.Count; i++){// 最后一个身体移动前,标记当前网格位置为空地if (i == bodyList.Count - 1){FoodManager.instance.MarkGridList(bodyList[i].transform.position, false);}// ...// 每个身体移动后,标记当前网格位置FoodManager.instance.MarkGridList(bodyList[i].transform.position, true);}}
}

食物也会占用网格,每次生成食物时,也要标记网格。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class FoodManager : MonoBehaviour
{public void GenerateFood(){GameObject obj = Instantiate(food, transform);int x = Random.Range(borderLeft, borderRight + 1);int y = Random.Range(borderBottom, borderTop + 1);obj.transform.position = new Vector3(x, y, 0);// 标记食物占用的网格位置MarkGridList(obj.transform.position, true);}
}

筛选空地

在食物生成时,不能单纯用随机数来确定坐标位置,而是要从网格列表中,筛选未被占用的网格点,然后从这些网格点中随机取出一个位置。

定义一个 filterList,用来存储筛选后的网格点。

每次生成食物时,需要先清理 filterList,然后从网格列表中,筛选 Z 为 0(表示未被占用)的网格点,添加到筛选列表中。

然后再生成随机数,从筛选列表中取出网格点,赋值位置给生成的食物。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class FoodManager : MonoBehaviour
{// ...public List<Vector3> filterList;// ...public void GenerateFood(){// 清理筛选列表filterList.Clear();for (int i = 0; i < gridList.Count; i++){// 筛选未被占用的网格点if (gridList[i].z == 0){filterList.Add(gridList[i]);}}// 没有空地了if (filterList.Count == 0) return;// 随机取出一个空地int index = Random.Range(0, filterList.Count);Vector3 pos = filterList[index];GameObject obj = Instantiate(food, transform);// int x = Random.Range(borderLeft, borderRight + 1);// int y = Random.Range(borderBottom, borderTop + 1);// obj.transform.position = new Vector3(x, y, 0);obj.transform.position = pos;// 标记食物占用的网格位置MarkGridList(obj.transform.position, true);}
}

至此,当蛇身越来越长时,也不会出现食物生成在蛇身上的情况了。

运行效果:

食物位置

相关文章:

Unity3D 小案例 像素贪吃蛇 02 蛇的觅食

Unity3D 小案例 像素贪吃蛇 第二期 蛇的觅食 像素贪吃蛇 食物生成 在场景中创建一个 2D 正方形&#xff0c;调整颜色&#xff0c;添加 Tag 并修改为 Food。 然后拖拽到 Assets 文件夹中变成预制体。 创建食物管理器 FoodManager.cs&#xff0c;添加单例&#xff0c;可以设置…...

【sgCreateCallAPIFunction】自定义小工具:敏捷开发→调用接口方法代码生成工具

<template><div :class"$options.name" class"sgDevTool"><sgHead /><div class"sg-container"><div class"sg-start"><div style"margin-bottom: 10px">调用接口方法定义列表</div…...

京东商品详情的 API 探秘与应用

在当今数字化的商业世界中&#xff0c;获取准确而详细的商品信息对于开发者、商家以及消费者都具有至关重要的意义。京东作为国内领先的电商平台之一&#xff0c;提供了丰富的商品资源和强大的 API 接口&#xff0c;让我们能够轻松获取京东商品的详情信息。本文将带你深入了解如…...

功能测试干了三年,快要废了。。。

8年前刚进入到IT行业&#xff0c;到现在学习软件测试的人越来越多&#xff0c;所以在这我想结合自己的一些看法给大家提一些建议。 最近聊到软件测试的行业内卷&#xff0c;越来越多的转行和大学生进入测试行业&#xff0c;导致软件测试已经饱和了&#xff0c;想要获得更好的待…...

【C++】多态的认识和理解

个人主页 文章目录 ⭐一、多态的概念&#x1f384;二、多态的定义及实现1.多态的构成2.实现多态的条件3.虚函数的概念4.虚函数的重写和覆盖5.析构函数的重写6.协变7.override和 final关键字8.重载、重写/覆盖、隐藏这三者的区别 &#x1f3e0;三、纯虚函数和抽象类的关系&#…...

linux-安全管理-用户认证

Linux 安全管理&#xff1a;用户认证 一、概述 用户认证是 Linux 安全管理的核心部分&#xff0c;确保系统能够识别并验证合法用户&#xff0c;同时阻止未经授权的访问。Linux 提供了多种用户认证机制&#xff0c;包括用户名和密码认证、基于密钥的认证、双因素认证&#xff…...

webpack5 构建优化方案看这篇就够了!【Node.js进阶】

无论在面试还是内部晋升&#xff0c;webpack 构建优化方案 一直都是非常重要的部分。 webpack5构建加持 一、项目完成目标二、搭建项目1. 安装koa、koa/router &#xff08;如果已经配置可路过&#xff09;2. 创建入口文件3. 安装构建依赖4. 在项目根目录添加 .babelrc 文件5. …...

esp32-C2 对接火山引擎实现智能语音(一)

目录 一、火山引擎大模型简介 1)火山引擎网址: 2)首先需要先注册火山引擎账号 3)语音识别——即语音转为文本 一句话识别 流式语音识别 录音文件识别标准版 录音文件识别极速版 4)语音合成——文本转音频 一、火山引擎大模型简介 火山引擎的智能语音技术,基于业界先…...

【MySQL-初级】mysql基础操作(账户、数据库、表的增删查改)

概述 数据备份与恢复 数据库备份&#xff1a;在cmd下 root用户&#xff1a;sudo mysqldump -u root -p Test > Test.sql普通用户&#xff1a;mysqldump -u zzz -p db_name > db_name.sql 数据库恢复 先创建一个空的数据库在cmd下&#xff1a;sudo mysql -u root -p d…...

centos bash脚本一键运行安装go环境

复制到install_go.sh直接bash install_go.sh运行就完了 echo ----------安装go环境 wget https://go.dev/dl/go1.21.13.linux-amd64.tar.gz tar -zxvf go1.21.13.linux-amd64.tar.gzmkdir /srv cp -r go /srv/echo "PATH$PATH:/srv/go/bin ">> ~/.bashrc echo…...

vue2制作高复用页面

记录一下页面搭建记录&#xff0c;利用vue2组件化开发的思想。这个页面适合于大部分信息管理系统~。模板固定&#xff0c;每次使用&#xff0c;直接修改表单表格参数&#xff0c;api接口等。 以上图页面为例&#xff0c;一个基础数据信息页面可以分为&#xff0c;分类&#xff…...

Feed流系统重构:架构篇

重构对我而言&#xff0c;最大的乐趣在于解决问题。我曾参与一个C#彩票算奖系统的重构&#xff0c;那时系统常因超时引发用户投诉。接手任务时&#xff0c;我既激动又紧张&#xff0c;连续两天几乎废寝忘食地编码。结果令人振奋&#xff0c;算奖时间从一小时大幅缩短至十分钟。…...

Android 后台服务之Persistent 属性

在 Android 开发中,有时我们需要后台服务持续运行,以保持应用的某些功能。例如,音乐播放器需要在后台播放音乐,或者健康应用需要持续跟踪用户的运动数据。后台服务是 Android 中的一种组件,它不与用户界面交互,能够在后台执行长时间运行的任务。由于 Android 系统的资源管…...

STM32+ESP01连接到机智云

机智云,全球领先的智能硬件软件自助开发及物联网(iot)云服务平台。机智云平台为开发者提供了自助式智能硬件开发工具与开放的云端服务。通过傻瓜化的自助工具、完善的SDK与API服务能力最大限度降低了物联网硬件开发的技术门槛&#xff0c;降低开发者的研发成本&#xff0c;提升…...

电脑实时监控软件有哪些?七个电脑屏幕监控软件任你选择

电脑实时监控软件种类繁多&#xff0c;每款软件都有其独特的功能和适用场景。 以下是七个备受推荐的电脑屏幕监控软件&#xff0c;供您选择&#xff1a; 1.安企神&#xff1a; 功能&#xff1a;它是一款国内领先的企业级电脑监控解决方案&#xff0c; 提供实时屏幕监控、 文…...

信奥学习规划(CSP-J/S)

CSP-J组学习路线规划 CSP-S组学习规划...

【Linux取经之路】编译器gcc/g++的使用 调试器gdb的使用

目录 背景知识 编译器gcc/g的安装 编译器gcc/g的使用 调试器gdb的使用 cgdb 条件断点 背景知识 子曰&#xff1a;“温故而知新”。在谈gcc/g的使用之前&#xff0c;我们先来复习编译的4个阶段&#xff0c;也算是为下面的内容做一些铺垫&#xff0c;请看思维导图。 编译…...

自动化流程机器人(RPA)

自动化流程机器人&#xff08;RPA&#xff09;正逐渐成为企业提高效率和降低成本的强有力工具。 一、RPA的概念 自动化流程机器人&#xff08;Robotic Process Automation&#xff0c;简称RPA&#xff09;是一种利用软件机器人&#xff08;Robot&#xff09;模拟和执行复杂任务…...

Unity persistentDataPath使用案例

Unity persistentDataPath使用案例 一、Application.persistentDataPath 1、概念 persistentDataPath&#xff1a;此属性用于返回一个持久化数据存储目录的路径&#xff0c;可以在此路径下存储一些持久化的数据文件&#xff1b;是一个可读写的目录&#xff1b;此文件夹在Edi…...

Android 测试手册

1. 介绍 Android 测试是确保应用程序质量的重要步骤。它包括不同类型的测试&#xff0c;用于验证应用程序的功能、性能、安全性和用户体验。这个手册将指导你了解和实施 Android 测试的主要方法和工具。 2. 测试类型 2.1 单元测试 目的&#xff1a;验证单个组件&#xff08…...

各大平台统遭入侵??区块链市场遭攻击损失近3亿!

今年&#xff0c;全球发生多起骇人听闻的勒索入侵软件攻击事件&#xff0c;黑客组织利用各种手段和技术&#xff0c;不断试图突破网络安全防线&#xff0c;窃取敏感信息、破坏系统运行&#xff0c;甚至进行勒索和敲诈&#xff0c;使得网络安全问题日益凸显其重要性和紧迫性。 S…...

Java泛型(“代码模板”,一套代码套用各种类型)

1.什么是泛型 a.定义 i.如果不用泛型定义&#xff0c;在使用ArrayList时需要为每个class编写特定类型代码。 ii.泛型就是定义一种模板&#xff0c;既实现了编写一次&#xff0c;万能匹配&#xff0c;又通过编译器保证了类型安全。 iii.编写模板代码来适应任意…...

速响低代码平台:升级营销管理系统,开启高效无忧新体验!

当前日新月异的商业环境&#xff0c;企业面临着前所未有的挑战与机遇。随着市场竞争的日益加剧和企业业务的不断拓展&#xff0c;传统的营销方式和管理手段逐渐显露出其局限性&#xff0c;难以适应快速变化的市场需求。 数据收集难&#xff1a;传统的营销管理缺乏对客户数据的收…...

Gitlab升级14.0.12-->14.3.6遇到的gitlab-ctl reconfigure错误

问题描述 在按照官方文档升级路线11.0.2>17.2.2的过程中&#xff0c;升级14.0.12–》14.3.6时遇到一个错误&#xff1a; Running handlers: There was an error running gitlab-ctl reconfigure:rails_migration[gitlab-rails] (gitlab::database_migrations line 51) had…...

JDBC导图

思维歹徒 一、使用步骤 二、SQL注入 三、数据库查询&#xff08;查询&#xff09; 四、数据库写入&#xff08;增删改&#xff09; 五、Date日期对象处理 六、连接池使用 创建连接是从连接池拿&#xff0c;释放连接是放回连接池 七、事务和批次插入 八、Apache Commons DBUtil…...

飞思实验室与中飞院联合开发教学课程,校企联袂共绘教育蓝图

近日&#xff0c;飞思实验室与中国民用航空飞行学院&#xff08;以下简称“中飞院”&#xff09; 航空电子电气学院合作&#xff0c;共同开发《无人智能视觉导航控制技术》、《多旋翼无人飞行器集群系统》实验课程。这一举措旨在深化校企融合&#xff0c;学校通过引入企业带来的…...

Telephony Call

1、Telephony 架构 Telephony整体架构和Android架构一样的,包括APP层,框架层,HAL层,内核层。 其中HAL层不同平台实现方式不同,其中MTK SPRD 平台使用AT通道的方式实现,高通使用QMI方式实现。 2、通话业务介绍 APP包括Dialer.apk、TeleService.apk、Tele…...

Python--TCP/UDP通信

文章目录 前言一、pandas是什么&#xff1f;二、使用步骤 1.引入库2.读入数据总结 一.客户端与服务端通信原理 1. 服务器端 服务器端的主要任务是监听来自客户端的连接请求&#xff0c;并与之建立连接&#xff0c;然后接收和发送数据。 创建套接字&#xff1a;首先&#xff0…...

【已解决】请教 “Sa-Token 集成 xxl-job,报错:非 web 上下文无法获取 HttpServletRequest” 如何解决

1. xxl-job 报错日志 2024-09-11 17:19:04 [com.xxl.job.core.thread.JobThread#run]-[133]-[xxl-job, JobThread-3-1726046344528] <br>----------- xxl-job job execute start -----------<br>----------- Param: 2024-09-11 17:19:04 [com.xxl.job.core.thread…...

Redis——常用数据类型string

目录 常用数据结构&#xff08;类型&#xff09;Redis单线程模型Reids为啥效率这么高&#xff1f;速度这么快&#xff1f;&#xff08;参照于其他数据库&#xff09; stringsetgetMSET 和 MGETSETNX&#xff0c;SETEX&#xff0c;PSETEXincr&#xff0c;incrby&#xff0c;decr…...