教程:构建关卡选择场景

教程:构建关卡选择场景

今天的教程演示了如何为游戏构建关卡选择场景。关卡选择屏幕在游戏中很常见,这些游戏分为多个关卡,玩家可以从这些关卡恢复游戏或选择重玩以获得最高分。

levselect

设置

本教程模块建立在 Corona Composer API 之上。它假设我们有一个场景模块 game.lua,当选择特定关卡时加载该模块,以及一个名为 menu.lua 的场景,如果玩家决定不玩关卡,则会显示该场景。

我们还需要一个可以在多个场景中访问的通用“数据”表。以下代码加载一个 mydata.lua 文件,该文件模拟了“再见全局变量!”教程中概述的简单数据模块

这看起来比实际情况复杂。下半部分只是为了本教程的目的而预先填充表 - 这将使我们能够看到结果,而无需围绕它构建一个功能齐全的游戏。关键的表成员在顶部

  • .maxLevels — 游戏的关卡最大数量(包含在关卡选择屏幕中)。
  • .settings — 将保存各种玩家数据和其他数据的数据表。
  • .settings.currentLevel — 玩家当前正在玩的关卡。
  • .settings.unlockedLevels — 玩家达到的(完成的)最高关卡。
  • .settings.soundOn / .settings.musicOn — 玩家音频偏好的布尔值。
  • .settings.levels — 跟踪玩家每个关卡进度的表。在本教程中,只有 .stars 很重要,但我们也可以跟踪玩家每个关卡的分数。

可以使用 loadsave 模块saveTable() 调用轻松保存整个表

场景设置

现在基本数据模块已配置完毕,让我们看一下核心场景

深入研究

所有这些都基于标准的 Composer 场景。尽管代码有注释,但让我们检查每个部分及其功能

  1. 为了使本教程保持简单,没有所需的图形资源。相反,我们使用 display.newPolygon() API 生成矢量星形。此 API 需要一个顶点数组,因此我们将其定义在场景的主代码块中,以防需要在其他地方使用它。
  2. 接下来的两个函数处理按钮事件。由于我们的按钮使用 onEvent 处理程序,我们需要测试阶段以确保代码只执行一次。在每个处理程序函数中,我们简单地使用 composer.gotoScene() 跳转到相应的场景。
  3. handleLevelSelect() 函数内部,我们在 myData.settings 表中设置 currentLevel 值,以便可以从 game.lua 场景轻松访问。我们还调用 composer.removeScene() 来确保每次游戏场景都全新加载。
  4. 其余的魔法发生在 scene:create() 事件处理程序中。首先,我们创建一个背景,将其填充设置为白色,并在屏幕上居中。然后我们创建一个 widget.newScrollView() 来容纳按钮,以防我们有超过一整屏的按钮。按照设计,我们可以在屏幕上容纳大约 20 个按钮,而无需滚动。
  5. 接下来,我们初始化一些变量,这些变量用于计算每个按钮在屏幕上的位置(在滚动视图内)。这些变量包括按钮的 xy 坐标(xOffsetyOffset)以及当前行中按钮的计数(cellCount)。cellCount 变量从 1 开始,本教程将在每行放置 5 个按钮。
  6. 对于游戏中的最大关卡数(myData.maxLevels),我们执行一个循环,并为每个按钮生成一个 widget.newButton()。本教程使用基于矢量的方法生成按钮形状 - 在本例中为圆角矩形。
  7. 在循环中,我们还将按钮的文本标签设置为数组的索引,从而按顺序编号。我们使用相同的值来设置按钮的 id 值,该值在玩家选择关卡时使用。
  8. 下一段代码测试是否已经传入解锁关卡的数量。解锁的关卡将以全彩色显示,并启用按钮的触摸处理程序,而锁定的关卡将淡出并禁用。
  9. 如果我们想显示每个关卡获得的星星数,我们现在可以生成它们。在实际制作的游戏中,我们可能会使用图像而不是星形多边形。无论哪种方式,我们都会从保存的数据(myData.settings.level.stars)中读取获得的星星数,并为每个星星生成一个。每个星星都相对于按钮的 xy 值定位。
  10. 接下来,我们递增 xOffset 以将下一个按钮进一步定位到右侧。我们还递增行中的按钮数,如果该值超过每行的按钮数,则我们将 xOffset 重置为该行的第一个位置,递增 cellCount 变量,并添加绘制下一行的像素数(yOffset)。
  11. 循环结束后,我们将整个滚动视图插入到场景的视图组中,并将其居中放置在屏幕上。

结论

虽然本模块的某些部分可能看起来很复杂,但大多数过程都很简单。当然,对于实际制作的游戏,需要对变量和数据结构进行某些调整。同时,如果您想在本教程中尝试使用代码库,请在此处下载 here

标签
Rob Miracle
[email protected]

Rob 是 Corona Labs 的开发者关系经理。除了热衷于帮助其他开发者使用 Corona 制作出色的游戏之外,他还喜欢在业余时间制作游戏。Rob 自 1979 年以来一直在为个人电脑到大型机等各种平台编写游戏代码。他在游戏行业拥有超过 16 年的专业经验。

20 条评论
  • julien
    发布于 15:21, 8 月 12 日

    你难道不需要在 scene:destroy 中删除背景和按钮吗?

    这是否意味着需要在 create 之外声明它们?

    • Rob Miracle
      发布于 17:49, 8 月 12 日

      Composer(以及以前仍然使用的 Storyboard)将自动删除已添加到场景视图组(即 self.view 或本地化的“sceneGroup”)的显示对象。如果场景被删除,那么这些对象将被释放。你只需要担心可能仍在运行的计时器、native.* 对象、转换、可能仍在播放并具有回调的音频和运行时事件侦听器。像这样的作为显示对象一部分的简单触摸和点击侦听器由对象本身处理。

      如果你的代码需要,你可能需要在 create scene 之外访问这些项目,但对于本例,没有任何需要这样做的事情。例如,被触摸的按钮对象作为 event.target 传递给事件处理程序,因此我仍然可以从该函数访问单个按钮。

      Rob

  • Mo
    发布于 21:32, 8 月 12 日

    太棒的教程了!谢谢你 Rob。这正是我们所有人为游戏实现所需要的类型。我可以建议另一个对制作游戏的人有帮助的教程吗?除了这个关卡选择教程之外,我很想看到一个解释如何制作那些通过向左或向右滑动来选择项目的菜单的教程。它们通常用于选择增强道具或世界。通常它们是水平排列的多个框,你可以通过手指滑动向左/向右滚动。

    不确定我是否表达清楚了 🙂

    但是非常感谢你这个很棒的教程!

    Mo.

    • Rob Miracle
      发布于 16:55, 8 月 13 日

      好主意 Mo!我会看看我能想出什么。

      Rob

  • Peter Dwyer
    发布于 02:12, 8 月 13 日

    讽刺似乎是我生活中最近的一个奇怪主题。我实际上在过去几周写了其中一个,因为我想要某种选择关卡的方法,并且拒绝购买一些预制包来做本质上简单的任务。当然,如果你是一名游戏设计师和开发者,你很快就会设法将简单变成一场消耗战。从关卡组到自由漫游选择地图,如糖果粉碎和植物大战僵尸 2,添加了所有的功能。

    >_<

  • Mo
    发布于 23:37, 8 月 14 日

    @Rob。太酷了,谢谢!

    Mo

  • Jeff Zivkovic
    发布于 16:52, 8 月 17 日

    很棒的教程。

    但我认为我错过了一些关于使用 composer 的非常基础的知识。我不知道创建会触发侦听器的预期场景的语法。我一直在寻找使用 composer 的示例,这样我就可以看到语法和用法,以实际创建场景并让这个关卡选择运行起来。

    • Jeff Zivkovic
      发布于 16:53, 8 月 17 日

      有人可以指导我找到一个使用 composer 的完整应用程序示例吗?

      非常感谢,

      Jeff

      • Jeff Zivkovic
        发布于 16:58, 8 月 17 日

        抱歉。没事了。我找到了 Corona 下载中附带的示例。

        感觉很不好意思。

  • Jeff Zivkovic
    发布于 06:07, 8 月 18 日

    好的。基本上运行起来了。

    但我不知道为什么我看不到按钮的笔画和填充。我可以看到星星和单词取消。当点击时(也就是当它处于“over”模式时),关卡编号会短暂显示。但是按钮的圆角矩形是白色背景上的白色。有什么想法吗?

    谢谢,

    Jeff

    • Rob Miracle
      发布于 15:33, 8 月 18 日

      请在论坛中提出这个问题。我们可能需要你发布代码,而论坛评论不是一个好的地方。

      Rob

  • Lori
    发布于 07:58, 10 月 10 日

    我能够设置按钮,但我在让它们转到正确的关卡时遇到了问题。我的游戏中每个关卡的游戏面板都是唯一的,所以我为每个关卡都有场景:level1.lua, level2.lua 等。我正在尝试修改按钮处理程序代码来执行此操作。下面是我认为需要修改的代码。我尝试了 if 语句,例如 if buttons.id == 1 then...我会将单词“game”更改为“level1”。我很感谢任何人的帮助

    -- 按钮处理程序以转到选定的关卡
    local function handleLevelSelect( event )
    if ( “ended” == event.phase ) then
    -- ‘event.target’ 是按钮,‘.id’ 是一个数字,指示要转到哪个关卡。
    -- ‘game’ 场景将使用此设置来确定要加载哪个关卡。
    -- 这也可以通过传递参数来完成。
    myData.settings.currentLevel = event.target.id

    -- 清理游戏场景,以便我们重新开始
    composer.removeScene( “game”, false )

    -- 转到游戏场景
    composer.gotoScene( “game”, { effect=”crossFade”, time=333 } )
    end
    end

    • Rob Miracle
      发布于 19:46, 10 月 10 日

      尝试

      composer.gotoScene( “level” .. tostring(event.target.id), { effect=”crossFade”, time=333 } )

  • Sobh
    发布于 16:03, 6 月 13 日


    谢谢教程

    我正面临一个问题。我正在尝试使用图像将 fillColor 更改为
    defaultFile = “lock.png”,
    overFile = “lock.png”,

    以便根据关卡是否通过来放置锁定或解锁图像

    但无论我做什么,defaultFile 和 overFile 都不起作用。

    我删除了 fillColor,但它仍然显示白色!

  • Sobh
    发布于 04:38, 6 月 19 日

    好的,我找到了问题
    对于遇到此问题的任何人
    你可以删除 shape 行并创建一个带有锁定形状的矩形图像,并将其用作默认文件。

  • Guest
    发布于 05:57, 6 月 5 日

    我下载了文件,当我在模拟器中打开它时,会出现错误消息
    “错误:运行时错误
    14:54:00.051 找不到模块 ‘utility’
    14:54:00.051 没有字段 package.preload[‘utility’]”

    你在教程中没有提到任何关于它的内容?

    • Rob Miracle
      发布于 11:15, 6 月 5 日

      在 game.lua 中有一行:local utility = require(“utility”)
      只需删除该行,一切就都准备好了。

  • Jonas
    发布于 08:59, 8 月 27 日

    你好!感谢你这个很棒的教程!我编程游戏玩得很开心!🙂
    我在我的游戏中做了这个,功能完全相同,我复制粘贴了代码,我唯一更改的是等于 “i” 的 “id” 变量,所以现在它是一个数字,而不是一个字符串。但是有些事情出错了:当我单击一个关卡按钮时,例如 3,它会将 id 存储在 myData.settings.currentLevel 中,它变为 3。但是当 game.lua 加载时,gameData.lua 中的所有变量都会重置为文件中的原始变量。所以 gameData.settings.currentLevel 再次变为 1。你知道我哪里做错了吗?谢谢!

    • Jonas
      发布于 09:02, 8 月 27 日

      抱歉,我的评论中有错字,第二次我说的是 “gameData” 而不是 “myData”,我的意思是 “myData”。

    • Rob Miracle
      发布于 11:08, 8 月 28 日

      这可能最好在论坛中提出的问题。首先,将其更改为 .i 而不是 .id 以使其成为一个数字是不必要的步骤。你可以只将 id 设置为数字。你是否在 game.lua 中读取保存的设置,并且可能没有在 levelselect.lua 中保存设置?这会导致这个问题。通常,你只需要在 main.lua 中加载设置,因为 myData 表将在整个应用程序中持续存在。但是,你应该在每次进行需要保留的更改时保存设置。