love2d-core

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

LÖVE (Love2D) Core

LÖVE (Love2D) 核心指南

Set up and debug the foundation of a LÖVE game in Lua: the callback loop, frame-rate- independent movement, input, and screen states. Targets LÖVE 11.5.
使用Lua搭建并调试LÖVE游戏的基础框架:包括回调循环、帧率无关的移动、输入处理以及屏幕状态管理。本文针对LÖVE 11.5版本。

When to use

适用场景

  • Use when starting a LÖVE game, wiring up
    main.lua
    /
    conf.lua
    , or fixing the core loop, movement that runs at the wrong speed, input handling, or screen switching.
  • Use when the workspace has
    main.lua
    calling
    love.*
    , a
    conf.lua
    , or a
    .love
    file.
When not to use: Lua language questions unrelated to LÖVE; physics bodies/joints (LÖVE uses Box2D via
love.physics
— a separate concern); shader code (
love.graphics
GLSL is its own topic). For cross-engine save/load patterns, use
save-systems
.
  • 适用于启动LÖVE游戏项目、配置
    main.lua
    /
    conf.lua
    ,或是修复核心循环、移动速度异常、输入处理问题、屏幕切换逻辑时。
  • 适用于工作区中存在调用
    love.*
    main.lua
    conf.lua
    .love
    文件的场景。
不适用场景:与LÖVE无关的Lua语言问题;物理体/关节(LÖVE通过
love.physics
使用Box2D,属于独立范畴);着色器代码(
love.graphics
的GLSL是单独主题)。如需跨引擎的保存/加载模式,请使用
save-systems

Core workflow

核心工作流程

  1. Confirm the entry points. A LÖVE game runs
    main.lua
    ; it should define
    love.load()
    (one-time setup),
    love.update(dt)
    (state), and
    love.draw()
    (rendering). Window/version setup goes in
    conf.lua
    (run before modules load).
  2. Pin the version. Set
    t.version = "11.5"
    in
    conf.lua
    so LÖVE warns on mismatch.
  3. Drive all motion by
    dt
    (delta time, in seconds) so speed is frame-rate independent.
  4. Handle input two ways: polled (
    love.keyboard.isDown
    in
    update
    , for held keys) and event (
    love.keypressed
    callback, for discrete presses).
  5. Manage screens (menu, game, pause) with a small state stack rather than a pile of
    if
    flags — see Patterns and
    references/state-stack.md
    .
  6. Run and observe. Launch with
    love .
    from the project folder; verify the window, motion speed, and input on screen before assuming it works.
  1. 确认入口文件:LÖVE游戏运行
    main.lua
    ;该文件应定义
    love.load()
    (一次性初始化)、
    love.update(dt)
    (状态更新)和
    love.draw()
    (渲染)。窗口/版本配置需放在
    conf.lua
    中(在模块加载前运行)。
  2. 锁定版本:在
    conf.lua
    中设置
    t.version = "11.5"
    ,这样当版本不匹配时LÖVE会发出警告。
  3. 所有运动均由
    dt
    驱动
    (delta time,单位为秒),确保速度不受帧率影响。
  4. 两种输入处理方式:轮询式(在
    update
    中使用
    love.keyboard.isDown
    ,处理长按按键)和事件式(
    love.keypressed
    回调,处理单次按键)。
  5. 管理屏幕状态(菜单、游戏、暂停)使用小型状态栈,而非大量
    if
    判断——详见“模式”部分和
    references/state-stack.md
  6. 运行并观察:从项目文件夹中执行
    love .
    启动游戏;在假设功能正常前,先验证窗口显示、移动速度和屏幕输入是否正常。

Patterns

模式示例

1.
main.lua
skeleton (the callback loop + input)

1.
main.lua
骨架(回调循环 + 输入处理)

lua
-- main.lua — LÖVE calls these callbacks for you. Colors are 0–1 in LÖVE 11.x.
function love.load()
    -- One-time setup. speed is in PIXELS PER SECOND, not per frame.
    player = { x = 100, y = 100, size = 40, speed = 220 }
    love.graphics.setBackgroundColor(0.1, 0.1, 0.12)
end

function love.update(dt)
    -- Polled input: good for continuous movement while a key is held.
    if love.keyboard.isDown("right") then player.x = player.x + player.speed * dt end
    if love.keyboard.isDown("left")  then player.x = player.x - player.speed * dt end
    if love.keyboard.isDown("down")  then player.y = player.y + player.speed * dt end
    if love.keyboard.isDown("up")    then player.y = player.y - player.speed * dt end
end

function love.draw()
    love.graphics.setColor(0.2, 0.8, 1.0)                 -- tint ON
    love.graphics.rectangle("fill", player.x, player.y, player.size, player.size)
    love.graphics.setColor(1, 1, 1)                       -- reset tint before text/images
    love.graphics.print("Arrow keys to move, Esc to quit", 10, 10)
end

function love.keypressed(key)
    -- Event input: fires once per physical press. Use for menus, jumps, toggles.
    if key == "escape" then love.event.quit() end
end
lua
-- main.lua — LÖVE会自动调用这些回调函数。LÖVE 11.x中颜色值范围为0–1。
function love.load()
    -- 一次性初始化。speed单位为像素/秒,而非像素/帧。
    player = { x = 100, y = 100, size = 40, speed = 220 }
    love.graphics.setBackgroundColor(0.1, 0.1, 0.12)
end

function love.update(dt)
    -- 轮询式输入:适合按键长按期间的持续移动。
    if love.keyboard.isDown("right") then player.x = player.x + player.speed * dt end
    if love.keyboard.isDown("left")  then player.x = player.x - player.speed * dt end
    if love.keyboard.isDown("down")  then player.y = player.y + player.speed * dt end
    if love.keyboard.isDown("up")    then player.y = player.y - player.speed * dt end
end

function love.draw()
    love.graphics.setColor(0.2, 0.8, 1.0)                 -- 设置颜色 tint ON
    love.graphics.rectangle("fill", player.x, player.y, player.size, player.size)
    love.graphics.setColor(1, 1, 1)                       -- 在绘制文本/图像前重置颜色
    love.graphics.print("Arrow keys to move, Esc to quit", 10, 10)
end

function love.keypressed(key)
    -- 事件式输入:每次物理按键按下触发一次。适用于菜单、跳跃、切换操作。
    if key == "escape" then love.event.quit() end
end

2. Frame-rate independence (the single most common bug)

2. 帧率无关性(最常见的错误)

lua
-- RIGHT: scaled by dt → same real-world speed at 30 or 240 FPS.
player.x = player.x + player.speed * dt
-- WRONG: "pixels per frame" → moves twice as fast at double the frame rate.
player.x = player.x + player.speed
lua
-- 正确:乘以dt → 在30或240 FPS下实际速度一致。
player.x = player.x + player.speed * dt
-- 错误:"像素/帧" → 帧率翻倍时移动速度也翻倍。
player.x = player.x + player.speed

3.
conf.lua
(window + version; runs before
main.lua
)

3.
conf.lua
(窗口 + 版本配置;在
main.lua
前运行)

lua
-- conf.lua — must be its own file; love.conf will NOT run from main.lua.
function love.conf(t)
    t.version = "11.5"             -- the LÖVE version this game targets (string "X.Y")
    t.window.title  = "My LÖVE Game"
    t.window.width  = 800
    t.window.height = 600
    t.window.vsync  = 1            -- number since 11.0: 1 = on, 0 = off, -1 = adaptive
    t.window.resizable = false
    t.modules.physics = false      -- disable modules you don't use to trim startup/memory
end
lua
-- conf.lua — 必须是独立文件;love.conf无法在main.lua中运行。
function love.conf(t)
    t.version = "11.5"             -- 游戏目标的LÖVE版本(字符串格式"X.Y")
    t.window.title  = "My LÖVE Game"
    t.window.width  = 800
    t.window.height = 600
    t.window.vsync  = 1            -- 11.0版本起为数值:1=开启,0=关闭,-1=自适应
    t.window.resizable = false
    t.modules.physics = false      -- 禁用未使用的模块以减少启动时间/内存占用
end

4. Color is 0–1 in LÖVE 11.x (not 0–255)

4. LÖVE 11.x中颜色值范围为0–1(而非0–255)

lua
-- LÖVE 11.x uses normalized floats. (Pre-11.0 code used 0–255 and will look wrong.)
love.graphics.setColor(1, 0, 0)                          -- opaque red
love.graphics.setColor(0.2, 0.8, 1.0, 0.5)               -- translucent cyan (alpha 0.5)
-- Need to convert old byte values? Use the helper instead of dividing by hand:
love.graphics.setColor(love.math.colorFromBytes(128, 234, 255))
lua
-- LÖVE 11.x使用归一化浮点数。(11.0之前的版本使用0–255,直接使用会显示异常。)
love.graphics.setColor(1, 0, 0)                          -- 不透明红色
love.graphics.setColor(0.2, 0.8, 1.0, 0.5)               -- 半透明青色(alpha值0.5)
-- 需要转换旧的字节值?使用辅助函数而非手动除法:
love.graphics.setColor(love.math.colorFromBytes(128, 234, 255))

5. Screen states (brief — full manager in references)

5. 屏幕状态(简化版——完整管理器见参考资料)

lua
-- A screen is a table with optional :update(dt), :draw(), :keypressed(key).
-- Keep the active screen on a stack so pause/menu overlays are trivial to pop.
local Stack = require("state_stack")   -- see references/state-stack.md for the module
function love.load()              Stack.push(require("screens.menu")) end
function love.update(dt)          Stack.current():update(dt) end
function love.draw()              Stack.current():draw() end
function love.keypressed(key)     Stack.current():keypressed(key) end
lua
-- 屏幕是一个包含可选:update(dt)、:draw()、:keypressed(key)方法的表。
-- 将活跃屏幕存储在栈中,这样暂停/菜单覆盖层可以轻松弹出。
local Stack = require("state_stack")   -- 模块实现见references/state-stack.md
function love.load()              Stack.push(require("screens.menu")) end
function love.update(dt)          Stack.current():update(dt) end
function love.draw()              Stack.current():draw() end
function love.keypressed(key)     Stack.current():keypressed(key) end

Pitfalls

常见陷阱

  • Speed varies with FPS → you forgot
    * dt
    . Every per-frame change to position, timers, or animation must be scaled by
    dt
    .
  • love.conf
    placed in
    main.lua
    → it silently does nothing. It must live in
    conf.lua
    , which LÖVE runs before loading modules.
  • Colors washed out or invisible → you used 0–255 values. In 11.x,
    setColor(255,0,0)
    clamps to white; use
    setColor(1,0,0)
    or
    love.math.colorFromBytes
    .
  • Everything tinted after one
    setColor
    → color is global and persists across draws. Reset with
    love.graphics.setColor(1, 1, 1)
    before drawing text/images you want untinted.
  • Nothing happens on key release/repeat
    love.keypressed(key, scancode, isrepeat)
    fires on press (and OS key-repeat); use
    love.keyreleased
    for release, and check
    isrepeat
    if you must ignore held-key repeats.
  • 速度随FPS变化 → 你忘记乘以
    dt
    。每帧对位置、计时器或动画的修改都必须乘以
    dt
  • love.conf
    放在
    main.lua
    → 它会静默失效。必须放在
    conf.lua
    中,LÖVE会在加载模块前运行该文件。
  • 颜色显示暗淡或不可见 → 你使用了0–255的数值。在11.x版本中,
    setColor(255,0,0)
    会被钳位为白色;请使用
    setColor(1,0,0)
    love.math.colorFromBytes
  • 调用一次
    setColor
    后所有内容都被着色
    → 颜色是全局状态,会在多次绘制中持续生效。在绘制不需要着色的文本/图像前,使用
    love.graphics.setColor(1, 1, 1)
    重置颜色。
  • 按键释放/重复时无响应
    love.keypressed(key, scancode, isrepeat)
    在按键按下(以及系统按键重复)时触发;如需处理按键释放,请使用
    love.keyreleased
    ,如果需要忽略长按重复,请检查
    isrepeat
    参数。

References

参考资料

  • For a complete push/pop screen-state manager (menu → game → pause, with delegated callbacks), read
    references/state-stack.md
    .
  • 如需完整的推入/弹出屏幕状态管理器(菜单→游戏→暂停,带委托回调),请阅读
    references/state-stack.md

Related skills

相关技能

  • save-systems
    — saving/loading game state (engine-agnostic).
  • input-systems
    — rebindable, multi-device input architecture.
  • pygame-core
    /
    phaser-core
    — the same loop concepts in other lightweight engines.
  • save-systems
    — 游戏状态的保存/加载(跨引擎通用)。
  • input-systems
    — 可重新绑定的多设备输入架构。
  • pygame-core
    /
    phaser-core
    — 其他轻量级引擎中的同类循环概念。