love2d-gamedev

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Love2D Game Development

Love2D游戏开发

Build polished 2D games with the Love2D framework—from first prototype to iOS release.
使用Love2D框架打造精致的2D游戏——从最初的原型到iOS版本发布。

Quick Reference

快速参考

TopicWhen to Use
Core ArchitectureUnderstanding game loop, callbacks, modules
Graphics & DrawingImages, colors, transforms, screen adaptation
AnimationSprite sheets, quads, frame timing
Tiles & MapsTile-based levels, map loading
CollisionAABB, circle, and separating axis collision
AudioSound effects, music, volume control
Project StructureFile organization, conf.lua, distribution
LibrariesPopular community libraries
iOS DeploymentBuild, touch controls, App Store

主题适用场景
核心架构理解游戏循环、回调函数、模块
图形与绘制图像、颜色、变换、屏幕适配
动画精灵表、四边形、帧时序
瓦片与地图基于瓦片的关卡、地图加载
碰撞检测AABB、圆形、分离轴碰撞检测
音频音效、音乐、音量控制
项目结构文件组织、conf.lua、发布打包
热门社区库
iOS部署构建、触摸控制、App Store上架

The Love2D Game Loop

Love2D游戏循环

Every Love2D game follows this pattern:
lua
function love.load()
    -- Called once at startup
    -- Load assets, initialize state
end

function love.update(dt)
    -- Called every frame
    -- dt = time since last frame (seconds)
    -- Update game logic here
end

function love.draw()
    -- Called every frame after update
    -- All rendering happens here
end
Key insight:
dt
(delta time) ensures consistent speed across frame rates.
lua
-- WRONG: Speed varies with frame rate
player.x = player.x + 5

-- RIGHT: 200 pixels per second, regardless of FPS
player.x = player.x + 200 * dt

每个Love2D游戏都遵循以下模式:
lua
function love.load()
    -- 启动时仅调用一次
    -- 加载资源,初始化状态
end

function love.update(dt)
    -- 每帧调用一次
    -- dt = 距上一帧的时间(秒)
    -- 在此处更新游戏逻辑
end

function love.draw()
    -- 在update之后每帧调用一次
    -- 所有渲染操作在此执行
end
核心要点
dt
(增量时间)确保在不同帧率下游戏速度保持一致。
lua
-- 错误写法:速度随帧率变化
player.x = player.x + 5

-- 正确写法:每秒移动200像素,与帧率无关
player.x = player.x + 200 * dt

Essential Patterns

核心模式

Loading and Drawing Images

图像加载与绘制

lua
function love.load()
    playerImage = love.graphics.newImage("player.png")
end

function love.draw()
    love.graphics.draw(playerImage, x, y)
    -- Full signature: draw(image, x, y, rotation, scaleX, scaleY, originX, originY)
end
lua
function love.load()
    playerImage = love.graphics.newImage("player.png")
end

function love.draw()
    love.graphics.draw(playerImage, x, y)
    -- 完整参数:draw(图像, x坐标, y坐标, 旋转角度, X轴缩放, Y轴缩放, 原点X, 原点Y)
end

Input Handling

输入处理

lua
-- Polling (check every frame)
function love.update(dt)
    if love.keyboard.isDown("left") then
        player.x = player.x - 200 * dt
    end
end

-- Event-based (fires once per press)
function love.keypressed(key)
    if key == "space" then
        player:jump()
    end
end
lua
-- 轮询模式(每帧检查)
function love.update(dt)
    if love.keyboard.isDown("left") then
        player.x = player.x - 200 * dt
    end
end

-- 事件驱动模式(每次按键触发一次)
function love.keypressed(key)
    if key == "space" then
        player:jump()
    end
end

Screen-Adaptive Positioning

屏幕自适应定位

Never hard-code screen dimensions:
lua
function love.load()
    screenW, screenH = love.graphics.getDimensions()
end

function love.resize(w, h)
    screenW, screenH = w, h
end

function love.draw()
    -- Position relative to screen
    local centerX = screenW / 2
    local bottomY = screenH - 50
end

永远不要硬编码屏幕尺寸:
lua
function love.load()
    screenW, screenH = love.graphics.getDimensions()
end

function love.resize(w, h)
    screenW, screenH = w, h
end

function love.draw()
    -- 相对于屏幕定位
    local centerX = screenW / 2
    local bottomY = screenH - 50
end

Core Modules

核心模块

ModulePurposeKey Functions
love.graphics
Rendering
draw
,
rectangle
,
circle
,
print
,
setColor
love.audio
Sound
newSource
,
play
,
stop
,
setVolume
love.keyboard
Keyboard input
isDown
,
keypressed
callback
love.mouse
Mouse input
getPosition
,
isDown
, callbacks
love.touch
Touch input
touchpressed
,
touchmoved
,
touchreleased
love.filesystem
File I/O
read
,
write
,
getInfo
love.timer
Timing
getDelta
,
getTime
,
getFPS
love.window
Window control
setMode
,
getMode
,
setTitle
love.physics
Box2D physics
newWorld
,
newBody
,
newFixture

模块用途关键函数
love.graphics
渲染
draw
,
rectangle
,
circle
,
print
,
setColor
love.audio
音效
newSource
,
play
,
stop
,
setVolume
love.keyboard
键盘输入
isDown
,
keypressed
回调函数
love.mouse
鼠标输入
getPosition
,
isDown
, 回调函数
love.touch
触摸输入
touchpressed
,
touchmoved
,
touchreleased
love.filesystem
文件I/O
read
,
write
,
getInfo
love.timer
计时
getDelta
,
getTime
,
getFPS
love.window
窗口控制
setMode
,
getMode
,
setTitle
love.physics
Box2D物理引擎
newWorld
,
newBody
,
newFixture

Project Setup

项目设置

Minimal Project

最小项目结构

my-game/
├── main.lua      # Entry point (required)
└── conf.lua      # Configuration (optional but recommended)
my-game/
├── main.lua      # 入口文件(必填)
└── conf.lua      # 配置文件(可选但推荐)

conf.lua Template

conf.lua模板

lua
function love.conf(t)
    t.window.title = "My Game"
    t.window.width = 800
    t.window.height = 600
    t.version = "11.5"              -- Love2D version
    t.console = true                -- Enable console on Windows

    -- Disable unused modules for faster startup
    t.modules.joystick = false
    t.modules.physics = false
end
lua
function love.conf(t)
    t.window.title = "My Game"
    t.window.width = 800
    t.window.height = 600
    t.version = "11.5"              # Love2D版本
    t.console = true                # 在Windows上启用控制台

    -- 禁用未使用的模块以加快启动速度
    t.modules.joystick = false
    t.modules.physics = false
end

Running the Game

运行游戏

bash
undefined
bash
undefined

macOS

macOS

/Applications/love.app/Contents/MacOS/love /path/to/game
/Applications/love.app/Contents/MacOS/love /path/to/game

Create alias in ~/.zshrc

在~/.zshrc中创建别名

alias love="/Applications/love.app/Contents/MacOS/love"

---
alias love="/Applications/love.app/Contents/MacOS/love"

---

Common Patterns

常见模式

State Management

状态管理

lua
local gameState = "menu"  -- menu, playing, paused, gameover

function love.update(dt)
    if gameState == "playing" then
        updateGame(dt)
    end
end

function love.draw()
    if gameState == "menu" then
        drawMenu()
    elseif gameState == "playing" then
        drawGame()
    end
end
lua
local gameState = "menu"  # 菜单、游戏中、暂停、游戏结束

function love.update(dt)
    if gameState == "playing" then
        updateGame(dt)
    end
end

function love.draw()
    if gameState == "menu" then
        drawMenu()
    elseif gameState == "playing" then
        drawGame()
    end
end

Object-Oriented Entities

面向对象的实体

lua
local Player = {}
Player.__index = Player

function Player:new(x, y)
    return setmetatable({
        x = x, y = y,
        speed = 200,
        image = love.graphics.newImage("player.png")
    }, Player)
end

function Player:update(dt)
    if love.keyboard.isDown("right") then
        self.x = self.x + self.speed * dt
    end
end

function Player:draw()
    love.graphics.draw(self.image, self.x, self.y)
end

return Player
lua
local Player = {}
Player.__index = Player

function Player:new(x, y)
    return setmetatable({
        x = x, y = y,
        speed = 200,
        image = love.graphics.newImage("player.png")
    }, Player)
end

function Player:update(dt)
    if love.keyboard.isDown("right") then
        self.x = self.x + self.speed * dt
    end
end

function Player:draw()
    love.graphics.draw(self.image, self.x, self.y)
end

return Player

Camera/Viewport

相机/视口

lua
local camera = { x = 0, y = 0 }

function love.draw()
    love.graphics.push()
    love.graphics.translate(-camera.x, -camera.y)

    -- Draw world objects here
    drawWorld()

    love.graphics.pop()

    -- Draw UI here (not affected by camera)
    drawUI()
end

lua
local camera = { x = 0, y = 0 }

function love.draw()
    love.graphics.push()
    love.graphics.translate(-camera.x, -camera.y)

    -- 在此处绘制世界对象
    drawWorld()

    love.graphics.pop()

    -- 在此处绘制UI(不受相机影响)
    drawUI()
end

Anti-Patterns to Avoid

需要避免的反模式

Don'tWhyDo Instead
Hard-code coordinatesBreaks on different screensUse percentages or anchors
Forget
dt
in movement
Speed varies with frame rateMultiply by
dt
Load assets in
update
/
draw
Loads every frame, kills performanceLoad once in
love.load
Use global variables everywhereHard to track, name collisionsUse local variables and modules
Test only on desktopTouch behaves differentlyTest on device early

不要做原因正确做法
硬编码坐标在不同屏幕上会失效使用百分比或锚点
移动时忘记
dt
速度随帧率变化乘以
dt
update
/
draw
中加载资源
每帧都加载,严重影响性能
love.load
中仅加载一次
到处使用全局变量难以追踪,容易出现命名冲突使用局部变量和模块
仅在桌面端测试触摸操作表现不同尽早在设备上测试

iOS Development

iOS开发

For iOS deployment, see the iOS Overview which covers:
  • Build Setup - Xcode project, libraries, signing
  • Touch Controls - Virtual joysticks, buttons, gestures
  • Xcode Project Structure - Manual pbxproj editing
Quick iOS checklist:
  1. Download Love2D iOS source + Apple libraries
  2. Copy libraries to Xcode project
  3. Fix deployment target (8.0 → 15.0)
  4. Create
    game.love
    (zip of Lua files)
  5. Add
    game.love
    to Xcode bundle resources
  6. Configure signing and deploy

关于iOS部署,请查看iOS概述,其中涵盖:
  • 构建设置 - Xcode项目、库、签名
  • 触摸控制 - 虚拟摇杆、按钮、手势
  • Xcode项目结构 - 手动编辑pbxproj
iOS快速检查清单
  1. 下载Love2D iOS源码 + Apple库
  2. 将库复制到Xcode项目
  3. 修改部署目标(从8.0改为15.0)
  4. 创建
    game.love
    (Lua文件的压缩包)
  5. game.love
    添加到Xcode的捆绑资源中
  6. 配置签名并部署

Philosophy

设计理念

Love2D makes game development joyful through simplicity:
  1. Lua is approachable - Dynamic typing, clean syntax, fast iteration
  2. The API is consistent - Functions follow predictable patterns
  3. You own the game loop - No hidden magic, full control
  4. Cross-platform by default - Same code runs on Windows, macOS, Linux, iOS, Android
The goal isn't just "make it work." The goal is "make it feel great."
Smooth animations, responsive controls, adaptive layouts—that's the standard for polished games.
Love2D通过简洁性让游戏开发充满乐趣:
  1. Lua易于上手 - 动态类型、简洁语法、快速迭代
  2. API保持一致 - 函数遵循可预测的模式
  3. 完全掌控游戏循环 - 无隐藏逻辑,全面控制
  4. 默认跨平台 - 相同代码可在Windows、macOS、Linux、iOS、Android上运行
目标不仅仅是“能运行”,而是“体验出色”。
流畅的动画、响应迅速的控制、自适应布局——这些是精致游戏的标准。