惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

S
Schneier on Security
A
Arctic Wolf
S
Security Affairs
O
OpenAI News
SecWiki News
SecWiki News
TaoSecurity Blog
TaoSecurity Blog
H
Heimdal Security Blog
T
Threat Research - Cisco Blogs
Hacker News: Ask HN
Hacker News: Ask HN
N
News | PayPal Newsroom
Google Online Security Blog
Google Online Security Blog
C
Cisco Blogs
The Hacker News
The Hacker News
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
C
CXSECURITY Database RSS Feed - CXSecurity.com
P
Privacy International News Feed
V
Vulnerabilities – Threatpost
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
酷 壳 – CoolShell
酷 壳 – CoolShell
H
Hacker News: Front Page
T
Tenable Blog
T
The Exploit Database - CXSecurity.com
Recent Commits to openclaw:main
Recent Commits to openclaw:main
Spread Privacy
Spread Privacy
人人都是产品经理
人人都是产品经理
www.infosecurity-magazine.com
www.infosecurity-magazine.com
V2EX - 技术
V2EX - 技术
L
LINUX DO - 最新话题
The GitHub Blog
The GitHub Blog
博客园 - 三生石上(FineUI控件)
T
The Blog of Author Tim Ferriss
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
V
Visual Studio Blog
The Cloudflare Blog
N
News and Events Feed by Topic
量子位
Google DeepMind News
Google DeepMind News
Application and Cybersecurity Blog
Application and Cybersecurity Blog
L
LINUX DO - 热门话题
P
Palo Alto Networks Blog
Stack Overflow Blog
Stack Overflow Blog
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
Attack and Defense Labs
Attack and Defense Labs
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
Hacker News - Newest:
Hacker News - Newest: "LLM"
Apple Machine Learning Research
Apple Machine Learning Research
The Register - Security
The Register - Security
Microsoft Security Blog
Microsoft Security Blog
Know Your Adversary
Know Your Adversary
Webroot Blog
Webroot Blog

元视角

.NET 生态下的 Agent 框架选型:从 ReAct 到原生推理 - 元视角 从「能用」到「好用」:LLM 流式响应实现方式的探索之路 - 元视角 当我用 2000 条聊天记录,让 AI 为我画一幅自画像 - 元视角 基于 Supabase 的 AI 应用开发探索 - 元视角 微博 × MCP:社交媒体新玩法解锁 - 元视角 四点钟海棠花未眠 - 元视角 Semantic Kernel × MCP:智能体的上下文增强探索 - 元视角 基于 K-Means 聚类分析实现人脸照片的快速分类 - 元视角 容器技术驱动下的代码沙箱实践与思考 - 元视角 温故而知新:后端通用查询方案的再思考 - 元视角 浅议 CancellationToken 在前后端协同取消场景中的应用 - 元视角 Semantic Kernel 视角下的 Text2SQL 实践与思考 - 元视角 关于 ChatGPT 的流式传输,你需要知道的一切 - 元视角 RAG 的是与非、Rewrite 和 Rerank - 元视角 使用 EFCore 和 PostgreSQL 实现向量存储及检索 - 元视角 基于 LLaMA 和 LangChain 实践本地 AI 知识库 - 元视角 使用 llama.cpp 在本地部署 AI 大模型的一次尝试 - 元视角 如何为 Git 配置多个 SSH Key - 元视角 C# 使用 LibUsbDotNet 实现 USB 设备检测 - 元视角 基于 C# 实现样式与数据分离的打印方案 - 元视角 基于 SVG 的图形交互方案实践 - 元视角 前端视频播放技术概览 - 元视角 温故而知新,再话 Python 动态导入 - 元视角 后 GPT 时代,NLP 不存在了? - 元视角 视频是不能 P 的系列:使用 Milvus 实现海量人脸快速检索 - 元视角 GDI+下字体大小自适应方案初探 - 元视角 小爱音箱集成 ChatGPT 的不完全教程 - 元视角 程序员视角下的三体世界随想 - 元视角 关于 Docker 容器配置信息的渐进式思考 - 元视角 在 Docker 容器内集成 Crontab 定时任务 - 元视角 为你的服务器集成 LDAP 认证 - 元视角 似花还似非花 - 元视角 视频是不能 P 的系列:使用 Dlib 实现人脸识别 - 元视角 浅议分布式链路追踪与日志的整合 - 元视角 关于 Git 大文件上传这件小事 - 元视角 .NET 进程内队列 Channel 的入门与应用 - 元视角 使用 Fody 实现 .NET 的静态编织 - 元视角 .NET Core + ELK 搭建可视化日志分析平台(下) - 元视角 聊一聊前端图片懒加载背后的故事 - 元视角 支持外部链接跳转的 Vue Router 扩展实现 - 元视角 视频是不能 P 的系列:OpenCV 和 Dlib 实现表情包 - 元视角 不得不说的 ASP.NET Core 集成测试 - 元视角 再议 DDD 视角下的 EFCore 与 领域事件 - 元视角 Vue.js 前端项目容器化部署实践极简教程 - 元视角 再见,人间四月天 - 元视角 Python 图像风格化迁移助力画家梦想 - 元视角 利用 ASP.NET Core 中的标头传播实现分布式链路追踪 - 元视角 利用 gRPC 实现文件的上传与下载 - 元视角 七种武器:延迟队列的原理和实现总结 - 元视角 gRPC 流式传输极简入门指南 - 元视角 Envoy 集成 Jaeger 实现分布式链路追踪 - 元视角 浅议非典型 Web 应用场景下的身份认证 - 元视角 gRPC 借助 Any 类型实现接口的泛化调用 - 元视角 分布式丛林探险系列之 Redis 集群模式 - 元视角 分布式丛林探险系列之 Redis 主从复制模式 - 元视角 通过 Python 预测 2021 年双十一交易额 - 元视角 gRPC 搭配 Swagger 实现微服务文档化 - 元视角 SSL/TLS 加密传输与数字证书的前世今生 - 元视角 使用 Python 自动识别防疫健康码 - 元视角 你不可不知的容器编排进阶技巧 - 元视角 ASP.NET Core 搭载 Envoy 实现 gRPC 服务代理 - 元视角 再话 AOP,从简化缓存操作说起 - 元视角 ASP.NET Core 搭载 Envoy 实现微服务身份认证(JWT) - 元视角 ASP.NET Core 搭载 Envoy 实现微服务的监控预警 - 元视角 ASP.NET Core 搭载 Envoy 实现微服务的反向代理 - 元视角 ASP.NET Core gRPC 打通前端世界的尝试 - 元视角 EFCore 实体命名约定库:EFCore.NamingConventions - 元视角 ASP.NET Core gRPC 集成 Polly 实现优雅重试 - 元视角 ASP.NET Core gRPC 健康检查的探索与实现 - 元视角 ASP.NET Core gRPC 拦截器的使用技巧分享 - 元视角 SnowNLP 使用自定义语料进行模型训练 - 元视角 使用 HttpMessageHandler 实现 HttpClient 请求管道自定义 - 元视角 ABP vNext 的实体与服务扩展技巧分享 - 元视角 ABP vNext 对接 Ant Design Vue 实现分页查询 - 元视角 源代码探案系列之 .NET Core 跨域中间件 CORS - 元视角 源代码探案系列之 .NET Core 限流中间件 AspNetCoreRateLimit - 元视角 源代码探案系列之 .NET Core 并发限制中间件 ConcurrencyLimiter - 元视角 通过 EmbededFileProvider 实现 Blazor 的静态文件访问 - 元视角 低代码,想说爱你不容易 - 元视角 记一次失败的 ThoughtWorks 面试经历 - 元视角 从 C# 1.0 到 C# 9.0,历代 C# 语言特性一览 - 元视角 通过 Python 分析 2020 年全年微博热搜数据 - 元视角 基于 Python 和 Selenium 实现 CSDN 一键三连自动化 - 元视角 使用多线程为你的 Python 爬虫提速的 N 种姿势,你会几种? - 元视角 实现网页长截图的常见思路总结 - 元视角 温故而知新,由 ADO.NET 与 Dapper 所联想到的 - 元视角 视频是不能 P 的系列:OpenCV 人脸检测 - 元视角 作为技术宅的我,是这样追鬼滅の刃的 - 元视角 使用 Python 抽取《半泽直树》原著小说人物关系 - 元视角 厉害了!打工人用 Python 分析西安市职位信息 - 元视角 使用 dotTrace 对 .NET 应用进行性能分析与优化 - 元视角 一道 HashSet 面试题引发的蝴蝶效应 - 元视角 基于选项模式实现.NET Core 的配置热更新 - 元视角 Dapper.Contrib 在 Oracle 环境下引发 ORA-00928 异常问题的解决 - 元视角 .NET Core 中对象池(Object Pool)的使用 - 元视角 利用 MySQL 的 Binlog 实现数据同步与订阅(下):EventBus 篇 - 元视角 利用 MySQL 的 Binlog 实现数据同步与订阅(中):RabbitMQ 篇 - 元视角 利用 MySQL 的 Binlog 实现数据同步与订阅(上):基础篇 - 元视角 记一次从已损坏的 Git 仓库中找回代码的经历 - 元视角 .NET Core 原生 DI 扩展之属性注入实现 - 元视角
使用 Love2D 引擎开发贪吃蛇游戏 - 元视角
飞鸿踏雪 · 2015-03-10 · via 元视角

今天来介绍博主最近捣腾的一个小游戏“贪吃蛇”。“贪吃蛇”这个游戏相信大家都不会感到陌生吧。今天博主将通过Love2D这款游戏引擎来为大家实现一个简单的贪吃蛇游戏,在本篇文章当中我们将会涉及到“贪吃蛇”的基本算法、Lua 语言编程等基本的内容,希望能够对大家开发类似的游戏提供借鉴和思考,文章中如有不足之处,还希望大家能够谅解,因为博主的游戏开发基本就是这样慢慢摸索着学习,所以难免会有不足的地方。

游戏算法

我们首先来看看贪吃蛇是怎么移动的? 贪吃蛇游戏算法演示1 贪吃蛇游戏算法演示1 贪吃蛇游戏算法演示2 贪吃蛇游戏算法演示2 贪吃蛇游戏算法演示3 贪吃蛇游戏算法演示3 贪吃蛇游戏算法演示4 贪吃蛇游戏算法演示4 通过这四张图的演示,我们可以发现这样一个规律:

蛇的移动其实是将蛇身体的最后一个元素移动到第一个元素的位置

那么完成这样一个工作需要两个步骤:

1、将在蛇头位置插入一个新的元素 2、移除蛇尾位置的最后一个元素

好了,了解了蛇的移动后我们再来考虑一个问题,怎样判断蛇吃到了食物?思路和蛇的移动类似,主要考虑在蛇头插入的这个元素和食物的关系,如果这个元素的坐标和食物的坐标是相同的,那么就可以认为蛇吃到了食物,此时蛇的身体应该是变长的,所以只要在蛇头位置插入一个元素就可以了。反之,如果蛇没有吃到食物,那么蛇应该是移动的,所以就可以按照移动的方法来处理了。那么在蛇头位置插入的这个元素该如何确定呢?我们来看下面这段程序:

--计算下一个目标点
function getNextPoint()
  --计算下一个目标点
  snake = {}
  if (dir == 0) then
    snake.x = snakes[1].x
    snake.y = snakes[1].y - 20
  end
  if (dir == 1) then
    snake.x = snakes[1].x
    snake.y = snakes[1].y + 20
  end
  if (dir == 2) then
    snake.x = snakes[1].x - 20
    snake.y = snakes[1].y
  end
  if (dir == 3) then
    snake.x = snakes[1].x + 20
    snake.y = snakes[1].y
  end

  return snake
end

这里定义了 getNextPoint()的方法,目的是计算在蛇头位置添加的下一个元素,这里我们注意到根据蛇的移动方向(dir)的不同,其中 0 表示上、1 表示下、2 表示左、3 表示右,计算出下一个元素的位置,因为在这个游戏中网格大小是 20,所以这里可以直接根据坐标来计算一个元素的位置。snakes 是一个 table,保存的是当前的蛇的全部元素的坐标。通过维护这个 table,我们就可以利用绘图的函数绘制出蛇的身体,这样蛇就可以移动起来了。我们来看看蛇是怎样移动的:

--核心算法——蛇的移动
function SnakeUpdate()
  --获取元素个数
  local n = table.maxn(snakes)
  if (table.maxn(snakes) > 0) then
    if (getNextPoint().x == foodX and getNextPoint().y == foodY) then
      --将下一个目标点的位置插入表中
      table.insert(snakes, 1, getNextPoint())
      --将食物状态设置为BeEated
      foodState="BeEated"
    else
      --将下一个目标点的位置插入表中
      table.insert(snakes, 1, getNextPoint())
      --移除最后一个元素
      table.remove(snakes,n+1)
    end 
  end
end

在这里我们定义了一个 foodState 变量以保存食物的状态,当食物的状态为 BeEated 的时候表示食物被蛇吃掉了,此时应该重新生成一个食物的坐标,此时事物的状态将变成 WaitToEat。食物的坐标保存在 foodX 和 foodY 这两个变量中,大家可以到完整的代码中去查看。

游戏状态

我们知道蛇碰到四周墙壁的时候就会死亡,此时游戏结束。这个比较简单,只要判断蛇头的坐标和屏幕的关系就可以了。因为在这个游戏中屏幕的尺寸为 640X640,所以判断游戏是否结束的代码可以这样写:

--判断游戏状态
  if(snakes[1].x <= 0 or snakes[1].x >= 640 or snakes[1].y <= 0 or snakes[1].y >= 640) then
    gameState = 0
  else
    gameState = 1
  end

这里 gameState 为 0 表示游戏结束,gameState 为 1 表示游戏正常进行。

完整代码

在完成了这些核心的算法以后,剩下的事情就交给 Love2D 引擎来绘制吧,最后给出完整的程序代码:

--定义窗口宽度和高度
local w	= 640
local h	= 640
--定义网格单元大小
local unitSize = 20;

--方块的初始位置
local initX	= 320
local initY	= 320

--移动方向
local dir = 1

--贪吃蛇集合
local snakes = {}

--食物状态
--WaitToEat:绘制食物
--BeEated:随机生成食物
local foodState = "WaitToEat"

--游戏状态
--0:游戏结束
--1:游戏正常
local gameState = 1

--食物的位置
local foodX = 0
local foodY = 0

--Love2D加载事件
function love.load()
  --设置窗口标题
  love.window.setTitle("Love2D-贪吃蛇游戏")
  --设置窗口大小
  love.window.setMode(w,h)
  --定义字体
  local myFont = love.graphics.newFont(30)
  --设置字体
  love.graphics.setFont(myFont)
  --设置背景色
  love.graphics.setBackgroundColor(255,255,255,255)
  --设置线条类型为平滑
  love.graphics.setLineStyle("smooth")
  --设置线宽
  love.graphics.setLineWidth(0.1)

  --蛇的初始化(蛇的长度为5)
  for i=1,5 do
    local snake = { }
    snake.x = initX + (i-1) * 20
    snake.y = initY
    snakes[i] = snake
  end

  --食物初始化
  foodX = love.math.random(32-1)*20
  foodY = love.math.random(32-1)*20
end


--Love2D绘制事件
function love.draw()
  --绘制竖线
  love.graphics.setColor(0,0,0,255)
  for i = 0, w, unitSize do
    love.graphics.line(0,i,h,i)
  end
  --绘制横线
  for j = 0, h, unitSize do
    love.graphics.line(j,0,j,w)
  end

  --绘制蛇
  for i = 1,table.maxn(snakes) do
    love.graphics.setColor(0,0,255,255)
    love.graphics.rectangle("fill",snakes[i].x,snakes[i].y,20,20)
  end

  --绘制食物
  if(foodState == "WaitToEat") then
    love.graphics.setColor(255,0,0,255)
    love.graphics.rectangle("fill",foodX,foodY,20,20)
  end

  --如果游戏结束则显示GameOver
  if(gameState == 0) then
    love.graphics.setColor(255,0,0,255)
    love.graphics.print("Game Over",250,300)
  end
end 

--
function love.update(dt)
  --判断游戏状态
  if (snakes[1].x <= 0 or snakes[1].x >= 640 or snakes[1].y <= 0 or snakes[1].y >= 640) then
    gameState = 0
  else
    gameState = 1
  end

  --如果游戏状态为正常
  if (gameState == 1) then
    SnakeUpdate()
    FoodUpdate()
  end
end

--核心算法——蛇的移动
function SnakeUpdate(dt)
  --获取元素个数
  local n = table.maxn(snakes)
  if(table.maxn(snakes) > 0) then
    if(getNextPoint().x == foodX and getNextPoint().y == foodY) then
      --将下一个目标点的位置插入表中
      table.insert(snakes, 1, getNextPoint())
      --将食物状态设置为BeEated
      foodState="BeEated"
    else
      --将下一个目标点的位置插入表中
      table.insert(snakes, 1, getNextPoint())
      --移除最后一个元素
      table.remove(snakes,n+1)
    end 
  end
end

--随机生成食物
function FoodUpdate()
  --如果食物被蛇吃掉则重新生成食物
  if(foodState == "BeEated") then
    foodX=love.math.random(32-1)*20
    foodY=love.math.random(32-1)*20
    foodState = "WaitToEat"
   end
end

--根据玩家按下的键位定义不同的方向
function love.keypressed(key)
  if (key == "a") then
    dir = 2
  end
  if (key == "d") then
    dir = 3
  end
  if (key == "w") then
    dir = 0
  end
  if (key=="s") then
    dir = 1
  end
end

--计算下一个目标点
function getNextPoint()
  --计算下一个目标点
  local snake = {}
  if(dir == 0) then
    snake.x = snakes[1].x
    snake.y = snakes[1].y - 20
  end
  if(dir == 1) then
    snake.x = snakes[1].x
    snake.y = snakes[1].y + 20
  end
  if(dir == 2) then
    snake.x=snakes[1].x - 20
    snake.y=snakes[1].y
  end
  if(dir == 3) then
    snake.x = snakes[1].x + 20
    snake.y = snakes[1].y
  end

  return snake
end

将代码压缩成.love 文件后就可以运行了,我们来看看最终的效果:

Love2D 贪吃蛇游戏示例1 Love2D 贪吃蛇游戏示例1

Love2D 贪吃蛇游戏示例2 Love2D 贪吃蛇游戏示例2

本文的项目作为开源项目托管在 Github 上,可以通过Github来获取项目源代码。谢谢大家,今天的内容就是这样了。