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 正方形,调整颜色,添加 Tag 并修改为 Food。 然后拖拽到 Assets 文件夹中变成预制体。 创建食物管理器 FoodManager.cs,添加单例,可以设置…...
【sgCreateCallAPIFunction】自定义小工具:敏捷开发→调用接口方法代码生成工具
<template><div :class"$options.name" class"sgDevTool"><sgHead /><div class"sg-container"><div class"sg-start"><div style"margin-bottom: 10px">调用接口方法定义列表</div…...
京东商品详情的 API 探秘与应用
在当今数字化的商业世界中,获取准确而详细的商品信息对于开发者、商家以及消费者都具有至关重要的意义。京东作为国内领先的电商平台之一,提供了丰富的商品资源和强大的 API 接口,让我们能够轻松获取京东商品的详情信息。本文将带你深入了解如…...
功能测试干了三年,快要废了。。。
8年前刚进入到IT行业,到现在学习软件测试的人越来越多,所以在这我想结合自己的一些看法给大家提一些建议。 最近聊到软件测试的行业内卷,越来越多的转行和大学生进入测试行业,导致软件测试已经饱和了,想要获得更好的待…...
【C++】多态的认识和理解
个人主页 文章目录 ⭐一、多态的概念🎄二、多态的定义及实现1.多态的构成2.实现多态的条件3.虚函数的概念4.虚函数的重写和覆盖5.析构函数的重写6.协变7.override和 final关键字8.重载、重写/覆盖、隐藏这三者的区别 🏠三、纯虚函数和抽象类的关系&#…...
linux-安全管理-用户认证
Linux 安全管理:用户认证 一、概述 用户认证是 Linux 安全管理的核心部分,确保系统能够识别并验证合法用户,同时阻止未经授权的访问。Linux 提供了多种用户认证机制,包括用户名和密码认证、基于密钥的认证、双因素认证ÿ…...
webpack5 构建优化方案看这篇就够了!【Node.js进阶】
无论在面试还是内部晋升,webpack 构建优化方案 一直都是非常重要的部分。 webpack5构建加持 一、项目完成目标二、搭建项目1. 安装koa、koa/router (如果已经配置可路过)2. 创建入口文件3. 安装构建依赖4. 在项目根目录添加 .babelrc 文件5. …...
esp32-C2 对接火山引擎实现智能语音(一)
目录 一、火山引擎大模型简介 1)火山引擎网址: 2)首先需要先注册火山引擎账号 3)语音识别——即语音转为文本 一句话识别 流式语音识别 录音文件识别标准版 录音文件识别极速版 4)语音合成——文本转音频 一、火山引擎大模型简介 火山引擎的智能语音技术,基于业界先…...
【MySQL-初级】mysql基础操作(账户、数据库、表的增删查改)
概述 数据备份与恢复 数据库备份:在cmd下 root用户:sudo mysqldump -u root -p Test > Test.sql普通用户:mysqldump -u zzz -p db_name > db_name.sql 数据库恢复 先创建一个空的数据库在cmd下: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制作高复用页面
记录一下页面搭建记录,利用vue2组件化开发的思想。这个页面适合于大部分信息管理系统~。模板固定,每次使用,直接修改表单表格参数,api接口等。 以上图页面为例,一个基础数据信息页面可以分为,分类ÿ…...
Feed流系统重构:架构篇
重构对我而言,最大的乐趣在于解决问题。我曾参与一个C#彩票算奖系统的重构,那时系统常因超时引发用户投诉。接手任务时,我既激动又紧张,连续两天几乎废寝忘食地编码。结果令人振奋,算奖时间从一小时大幅缩短至十分钟。…...
Android 后台服务之Persistent 属性
在 Android 开发中,有时我们需要后台服务持续运行,以保持应用的某些功能。例如,音乐播放器需要在后台播放音乐,或者健康应用需要持续跟踪用户的运动数据。后台服务是 Android 中的一种组件,它不与用户界面交互,能够在后台执行长时间运行的任务。由于 Android 系统的资源管…...
STM32+ESP01连接到机智云
机智云,全球领先的智能硬件软件自助开发及物联网(iot)云服务平台。机智云平台为开发者提供了自助式智能硬件开发工具与开放的云端服务。通过傻瓜化的自助工具、完善的SDK与API服务能力最大限度降低了物联网硬件开发的技术门槛,降低开发者的研发成本,提升…...
电脑实时监控软件有哪些?七个电脑屏幕监控软件任你选择
电脑实时监控软件种类繁多,每款软件都有其独特的功能和适用场景。 以下是七个备受推荐的电脑屏幕监控软件,供您选择: 1.安企神: 功能:它是一款国内领先的企业级电脑监控解决方案, 提供实时屏幕监控、 文…...
信奥学习规划(CSP-J/S)
CSP-J组学习路线规划 CSP-S组学习规划...
【Linux取经之路】编译器gcc/g++的使用 调试器gdb的使用
目录 背景知识 编译器gcc/g的安装 编译器gcc/g的使用 调试器gdb的使用 cgdb 条件断点 背景知识 子曰:“温故而知新”。在谈gcc/g的使用之前,我们先来复习编译的4个阶段,也算是为下面的内容做一些铺垫,请看思维导图。 编译…...
自动化流程机器人(RPA)
自动化流程机器人(RPA)正逐渐成为企业提高效率和降低成本的强有力工具。 一、RPA的概念 自动化流程机器人(Robotic Process Automation,简称RPA)是一种利用软件机器人(Robot)模拟和执行复杂任务…...
Unity persistentDataPath使用案例
Unity persistentDataPath使用案例 一、Application.persistentDataPath 1、概念 persistentDataPath:此属性用于返回一个持久化数据存储目录的路径,可以在此路径下存储一些持久化的数据文件;是一个可读写的目录;此文件夹在Edi…...
Android 测试手册
1. 介绍 Android 测试是确保应用程序质量的重要步骤。它包括不同类型的测试,用于验证应用程序的功能、性能、安全性和用户体验。这个手册将指导你了解和实施 Android 测试的主要方法和工具。 2. 测试类型 2.1 单元测试 目的:验证单个组件(…...
从深圳崛起的“机器之眼”:赴港乐动机器人的万亿赛道赶考路
进入2025年以来,尽管围绕人形机器人、具身智能等机器人赛道的质疑声不断,但全球市场热度依然高涨,入局者持续增加。 以国内市场为例,天眼查专业版数据显示,截至5月底,我国现存在业、存续状态的机器人相关企…...
定时器任务——若依源码分析
分析util包下面的工具类schedule utils: ScheduleUtils 是若依中用于与 Quartz 框架交互的工具类,封装了定时任务的 创建、更新、暂停、删除等核心逻辑。 createScheduleJob createScheduleJob 用于将任务注册到 Quartz,先构建任务的 JobD…...
Nginx server_name 配置说明
Nginx 是一个高性能的反向代理和负载均衡服务器,其核心配置之一是 server 块中的 server_name 指令。server_name 决定了 Nginx 如何根据客户端请求的 Host 头匹配对应的虚拟主机(Virtual Host)。 1. 简介 Nginx 使用 server_name 指令来确定…...
Module Federation 和 Native Federation 的比较
前言 Module Federation 是 Webpack 5 引入的微前端架构方案,允许不同独立构建的应用在运行时动态共享模块。 Native Federation 是 Angular 官方基于 Module Federation 理念实现的专为 Angular 优化的微前端方案。 概念解析 Module Federation (模块联邦) Modul…...
Java入门学习详细版(一)
大家好,Java 学习是一个系统学习的过程,核心原则就是“理论 实践 坚持”,并且需循序渐进,不可过于着急,本篇文章推出的这份详细入门学习资料将带大家从零基础开始,逐步掌握 Java 的核心概念和编程技能。 …...
视觉slam十四讲实践部分记录——ch2、ch3
ch2 一、使用g++编译.cpp为可执行文件并运行(P30) g++ helloSLAM.cpp ./a.out运行 二、使用cmake编译 mkdir build cd build cmake .. makeCMakeCache.txt 文件仍然指向旧的目录。这表明在源代码目录中可能还存在旧的 CMakeCache.txt 文件,或者在构建过程中仍然引用了旧的路…...
iOS性能调优实战:借助克魔(KeyMob)与常用工具深度洞察App瓶颈
在日常iOS开发过程中,性能问题往往是最令人头疼的一类Bug。尤其是在App上线前的压测阶段或是处理用户反馈的高发期,开发者往往需要面对卡顿、崩溃、能耗异常、日志混乱等一系列问题。这些问题表面上看似偶发,但背后往往隐藏着系统资源调度不当…...
Qt 事件处理中 return 的深入解析
Qt 事件处理中 return 的深入解析 在 Qt 事件处理中,return 语句的使用是另一个关键概念,它与 event->accept()/event->ignore() 密切相关但作用不同。让我们详细分析一下它们之间的关系和工作原理。 核心区别:不同层级的事件处理 方…...
【Veristand】Veristand环境安装教程-Linux RT / Windows
首先声明,此教程是针对Simulink编译模型并导入Veristand中编写的,同时需要注意的是老用户编译可能用的是Veristand Model Framework,那个是历史版本,且NI不会再维护,新版本编译支持为VeriStand Model Generation Suppo…...
【Post-process】【VBA】ETABS VBA FrameObj.GetNameList and write to EXCEL
ETABS API实战:导出框架元素数据到Excel 在结构工程师的日常工作中,经常需要从ETABS模型中提取框架元素信息进行后续分析。手动复制粘贴不仅耗时,还容易出错。今天我们来用简单的VBA代码实现自动化导出。 🎯 我们要实现什么? 一键点击,就能将ETABS中所有框架元素的基…...
