第一章 — 创建项目

上一页 | 下一页

在本章中,我们将创建一个简单的基于点击的游戏,让气球保持在空中,并涵盖游戏开发的一些基础知识。对于新手或有一段时间没有编程的人来说,我们将尽可能地简化第一个项目。

使用 Solar2D

脚本编写

Solar2D 使用 Lua 脚本语言。如果您曾经使用任何语言进行过编程,您会发现 Lua 很容易上手。Lua 简介指南提供了 Lua 的概述,或者您可以在 YouTube 上学习 Lua。很快,您就能编写出您梦寐以求的出色应用程序!

如果您是编程新手,Lua 仍然很容易学习,本指南将帮助您入门。

文本编辑器

除了 Solar2D 之外,您还需要一个文本编辑器。有几种编辑器可供选择,选择编辑器就像选择汽车一样——每个人都有自己的喜好,您应该探索哪种编辑器最适合您。

如果您还没有喜欢的文本编辑器,建议使用以下选项

编辑器 附加组件包 macOS Windows
Sublime Text Solar2D Editor
Visual Studio Code Solar2d Autocomplete
Xcode Xcode Editor
Vim
ZeroBrane Studio
Notepad++
TextMate

创建项目

您的第一个项目将非常简单,但它将演示一些重要的概念。我们将制作一个简单的点击游戏,让气球保持在空中。

注意
  • 使用本指南的最佳方法是遵循每个步骤——输入代码、添加图像,并看到您的项目逐渐成型。这可能需要更多时间,但您将获得更好的理解。

  • 每章都包含一个可下载的文件,其中包含与该章相关的所有图像、音频文件和其他资源。本章的源文件可在此处获得。

启动项目

在 Solar2D 中创建新项目很容易。只需几个简单的步骤,您就可以制作您的第一个游戏!

  1. 打开 Solar2D 模拟器

  2. 从欢迎窗口中单击新建项目,或从文件菜单中选择新建项目…

  1. 对于项目/应用程序名称,键入 BalloonTap 并确保选中空白模板选项。将其他设置保留为默认值,然后单击确定 (Windows) 或下一步 (Mac)。这将在您指定的位置(文件夹)中创建您的第一个游戏的基本文件。这也是您放置所有应用程序文件/资源的文件夹,包括图像、程序文件等。

包含图像

对于此项目,您需要三个图像文件,放置在上面创建的 BalloonTap 项目文件夹中

文件 大小 (宽×高) 用途
background.png 360 × 570 背景——纯粹的装饰,这样我们就不会盯着黑色背景。
platform.png 300 × 50 平台/地板——防止气球从屏幕底部掉落。
balloon.png 112 × 112 气球。

为了快速入门,您可以下载并使用本章源文件中包含的默认图像。在存档中,您将找到上面列出的三个图像。

如果您选择为此项目或任何其他项目创建自己的图像,请注意以下基本图像准则

  • Solar2D 支持 PNG 和 JPG 格式。SVG 通过 Nano SVG 插件支持。
  • 图像不应包含嵌入的 ICC 配置文件。
  • 避免使用渐进式 JPG 文件,因为它们加载时间会更长。

加载背景

我们需要加载的第一个图像是背景。Solar2D 将屏幕上的所有内容按照从后到前的顺序进行分层放置,因此我们加载的第一个图像将位于之后加载的其他图像的后面。虽然有一些方法可以更改图像的分层顺序并将它们发送到显示堆栈的后面或前面,但我们将保持这个项目的简单性并按逻辑顺序加载它们。

使用您选择的文本编辑器,在您的项目文件夹中找到并打开 `main.lua` 文件。 `main.lua` 文件是基本的“核心程序文件”每个 Solar2D 项目,如果没有它,您就无法创建应用程序。这是每次运行应用程序时应用程序启动的 Lua 文件。

在这个 `main.lua` 文件中,输入高亮显示的命令

-----------------------------------------------------------------------------------------
--
-- main.lua
--
-----------------------------------------------------------------------------------------

local background = display.newImageRect( "background.png", 360, 570 )

此命令涉及几个方面。让我们分解一下

  • 第一个单词 `local` 是一个 Lua 命令,表示下一个单词将是一个**变量**。变量,就像您在数学课上学到的那样,用于存储一个值。在本例中,该值将是一个用作背景的图像。

请注意,`local` 始终是小写的,它在这里用于声明变量;例如,第一次使用变量时,应在变量前面添加 `local`。

  • 第二个单词 `background` 是我们变量的**名称**。任何时候我们想要更改存储在 `background` 中的图像,我们将使用这个变量名。

记住每次使用变量时都要使用不同的变量名。就像教室里每个人都叫“约翰”会让人感到困惑一样,对所有对象使用相同的变量名会在程序中造成混乱。

  • `=`(等号)用于将变量 `background` 分配给一个图像。

  • `display.newImageRect()`是 Solar2D API 之一。它用于从文件中加载图像,以便您可以在应用程序中使用它。有几种方法可以将图像加载到您的应用程序中,但 `display.newImageRect()` 的特殊之处在于它可以调整/缩放图像的大小(稍后将对此进行解释)。

  • 括号内是我们传递给 `display.newImageRect()` 的**参数**,有时也称为**参数**。第一个参数是我们想要加载的图像文件的名称,包括文件扩展名(`.png`)。

指定的名称必须与实际文件名完全匹配,包括区分大小写!例如,如果实际文件名是 `background.png`,**不要**将其输入为 `"BackGround.PNG"`。

接下来的两个参数 `360` 和 `570` 指定了我们希望背景图像的大小。 دراین مورد، ما به سادگی از ابعاد پیکسلی تصویر استفاده خواهیم کرد، اگرچه همانطور که در بالا ذکر شد، `display.newImageRect()` به شما امکان می دهد اندازه تصویر را از طریق این اعداد تغییر دهید/مقیاس بندی کنید.

背景的最后一步是将其定位在屏幕上的正确位置。紧跟您刚才输入的行,添加两个突出显示的命令

local background = display.newImageRect( "background.png", 360, 570 )
background.x = display.contentCenterX
background.y = display.contentCenterY

默认情况下,Solar2D 会将对象的中心定位在坐标点 `0,0` 处,该坐标点位于屏幕的左上角。但是,通过更改对象的 `x` 和 `y` 属性,我们可以将背景图像移动到新位置。

对于这个项目,我们将把背景放在屏幕的中心——但如果我们不知道哪个坐标值代表中心呢?幸运的是,Solar2D 为此提供了一些方便的快捷方式。当您指定值 `display.contentCenterX` 和 `display.contentCenterY` 时,Solar2D 将屏幕的中心坐标设置为 `background.x` 和 `background.y` 属性。

让我们检查一下您的代码的结果!保存您修改过的 `main.lua` 文件,然后,在模拟器中,使用⌘-R (Command-R)“重新启动”它。如果一切顺利,背景现在应该显示在屏幕中央。

如果您收到错误或看不到背景,则可能有多种原因

  • 其中一个命令输入错误。
  • 图像文件与 `main.lua` 不在同一个文件夹中。
  • 指定的文件名和/或扩展名不正确或大小写不匹配。

请记住,模拟器控制台窗口是检查和诊断代码中潜在错误的好地方。在 Windows 上,此面板始终在模拟器中可用。在 Mac 上,如果此窗口尚未打开,您可以通过选择**窗口** → **控制台**.

加载平台

来查看它。是时候加载平台了。这与加载背景非常相似。在您已经输入的三行代码之后,输入以下突出显示的命令

local background = display.newImageRect( "background.png", 360, 570 )
background.x = display.contentCenterX
background.y = display.contentCenterY

local platform = display.newImageRect( "platform.png", 300, 50 )
platform.x = display.contentCenterX
platform.y = display.contentHeight-25

您可能已经注意到,与背景相比有一个细微的变化:我们没有将平台定位在垂直中心,而是希望它靠近屏幕底部。通过使用命令 `display.contentHeight`,我们知道内容区域的高度。但请记住,`platform.y` 将对象的**中心**放置在指定位置。因此,由于此对象的高度为 50 像素,我们从该值中减去 25 像素,确保整个平台都可以在屏幕上看到。

保存您的 `main.lua` 文件并重新启动模拟器以查看平台图形。

加载气球

要加载气球,我们将遵循相同的过程。在前面的命令下方,键入以下几行

local balloon = display.newImageRect( "balloon.png", 112, 112 )
balloon.x = display.contentCenterX
balloon.y = display.contentCenterY

此外,为了使气球略微透明,我们将稍微降低对象的不透明度(alpha)。在下一行,将气球的 `alpha` 属性设置为 80%(`0.8`)

local balloon = display.newImageRect( "balloon.png", 112, 112 )
balloon.x = display.contentCenterX
balloon.y = display.contentCenterY
balloon.alpha = 0.8

保存您的 `main.lua` 文件并重新启动模拟器。现在屏幕中央应该有一个气球。

添加物理效果

是时候进入物理学了!Box2D 是包含的物理引擎,可用于构建应用程序和游戏。虽然制作游戏不需要使用物理学,但它可以更轻松地处理许多游戏情况。

包含物理学非常简单。在前面的行下方,添加以下命令

local physics = require( "physics" )
physics.start()

让我们更详细地解释这两行

  • 命令`local physics = require( "physics" )`将 Box2D 物理引擎加载到您的应用程序中,并将其与局部变量 `physics` 关联以供以后参考。这使您可以使用 `physics` 命名空间变量调用物理库中的其他命令,稍后您将看到。

  • physics.start() 正如你所想,它会启动物理引擎。

如果你保存并重新启动,你不会看到任何区别在你的游戏中……暂时还不会。这是因为我们还没有给物理引擎任何任务。为了让物理引擎工作,我们需要将创建的图像/对象转换为物理对象。这可以通过命令 physics.addBody 来完成。

local physics = require( "physics" )
physics.start()

physics.addBody( platform, "static" )

这告诉物理引擎将一个物理**“物体”**添加到存储在 platform 中的图像上。此外,第二个参数告诉 Solar2D 将其视为**静态**物理对象。这意味着什么?基本上,静态物理对象不受重力或其他物理力的影响,所以任何时候你有一个不应该移动的对象,将其类型设置为 "static"

现在,为气球添加一个物理物体。

local physics = require( "physics" )
physics.start()

physics.addBody( platform, "static" )
physics.addBody( balloon, "dynamic", { radius=50, bounce=0.3 } )

与平台相反,气球是一个**动态**物理对象。这意味着它会受到重力的影响,它会对与其他物理对象的碰撞做出物理反应,等等。在这种情况下,第二个参数 ("dynamic") 实际上是可选的,因为默认的物体类型已经是动态的,但我们在这里包含它以帮助学习过程。

physics.addBody 命令的最后一部分用于调整气球的物体属性——在本例中,我们将赋予它一个圆形形状并调整其弹跳/恢复值。参数必须放在花括号 ({}) 中(在 Lua 编程语言中称为**表**)。

  • 因为气球是一个圆形物体,所以我们为它分配一个值为 50radius(半径)属性。这个值基本上与我们的气球图像的大小相匹配,但如果你创建了自己的气球图像,你可能需要稍微调整它。

  • bounce(弹跳)值可以是任何非负的十进制或整数值。值为 0 表示气球没有弹跳,而值为 1 将使其以 100% 的碰撞能量反弹。如上所示,值为 0.3 将使其以 30% 的能量反弹。

注意
  • bounce 值大于 1 将使物体以超过 100% 的碰撞能量反弹。如果将值设置为大于 1,请务必小心,因为物体可能会迅速获得超出通常或预期值的动量。

  • 即使你将气球的 bounce 属性更改为 0,它仍然会从 platform 对象上弹开,因为默认情况下,对象的弹跳值为 0.2。要在此游戏中完全移除弹跳,请将气球**和**平台都设置为 bounce=0

保存你的 main.lua 文件并重新启动模拟器。作为一个有趣的实验,你可以尝试调整 bounce 值并重新启动项目以查看效果。

函数

此时,我们有一个气球落在平台上并轻微弹跳。这并不是很有趣,所以让我们把它变成一个游戏!为了让我们的气球点击游戏能够运行,我们需要能够在每次点击气球时向上推动它一点。

为了实现这种功能,编程语言使用所谓的**函数**。函数是简短的(通常)代码段,只有在我们告诉它们运行时才会运行,例如当玩家点击气球时。

让我们创建我们的第一个函数。

local function pushBalloon()

end

函数对于应用程序和游戏开发都是必不可少的,所以让我们检查一下基本结构。

  • 和之前一样,我们使用关键字 local 来声明函数。

  • 关键字 function 告诉 Solar2D 这是一个函数,它的命令集将被称为 pushBalloon

  • 末尾的括号 (()) 是必需的。在后面的章节中,我们将在这些括号中放入一些内容,但现在你可以保持原样。

  • 如上所述,函数是自包含的代码段(块),只有在我们告诉它们运行时才会运行。因此,每当你创建一个函数时,你**必须**使用关键字 end 关闭它。这告诉 Lua 函数已经结束。

很好,我们现在有了一个函数!然而,它目前是一个空函数,所以如果我们运行它,它实际上不会做任何事情。让我们通过在函数**内部**,在我们声明函数的地方(它的开头行)和结束关键字 end 之间添加以下代码行来解决这个问题。

local function pushBalloon()
    balloon:applyLinearImpulse( 0, -0.75, balloon.x, balloon.y )
end

当你在函数内部添加代码行时,缩进至少一个**制表符**或3-4四个空格被认为是良好的编程习惯。这使你的代码更具可读性,并且更容易在较长的程序中识别函数。

balloon:applyLinearImpulse 是一个非常酷的命令。当应用于像气球这样的动态物理对象时,它会在任何方向上对对象施加一个“推力”。我们传递的参数告诉物理引擎施加多大的力(水平和垂直方向),以及在对象的哪个位置施加力。

前两个参数 0-0.75 表示方向力的量。第一个数字是水平方向或**x** 方向,第二个数字是垂直方向或**y** 方向。因为我们只想向上推动气球(而不是向左或向右),我们使用 0 作为第一个参数。对于第二个参数,值为 -0.75,我们告诉物理引擎将气球向上推一点。这个数字的值决定了施加的力的大小:数字越大,力越大。

如右图所示,Solar2D 中的正**x** 值向**右**延伸,而正**y** 值向**下**延伸(与笛卡尔坐标系中向上延伸不同)。这就是为什么我们使用负值 (-0.75) 向上推动气球的原因。

第三个和第四个参数,balloon.xballoon.y,告诉物理引擎在哪里施加力,相对于气球本身。如果在气球中心以外的位置施加力,可能会导致气球向意想不到的方向移动或旋转。对于这个游戏,我们将使力集中在气球的中心。

就是这样!如果需要,我们可以在 pushBalloon() 函数中添加其他命令,但对于这个简单的游戏,我们只需要用少量力向上推动气球即可。

事件

事件是创造交互性的关键,在很多方面,Solar2D 是一个基于事件的框架,其中信息在特定事件期间被分派到**事件侦听器**。无论该事件是用户触摸对象/按钮、点击屏幕,还是(在本游戏中)点击气球,Solar2D 都可以通过触发事件来做出反应。

添加事件侦听器很容易——现在就按照函数进行操作

local function pushBalloon()
    balloon:applyLinearImpulse( 0, -0.75, balloon.x, balloon.y )
end

balloon:addEventListener( "tap", pushBalloon )

让我们检查一下这个新命令的结构

  • 首先,我们必须告诉 Solar2D 哪个对象参与了事件侦听器。对于这个游戏,我们想检测与 balloon 对象直接相关的事件。

  • 紧接着,添加一个冒号 (:),然后是 addEventListener。在 Lua 中,这被称为**对象方法**。本质上,冒号后面的 addEventListener 告诉 Solar2D 我们想要向冒号前面的 balloon 添加一个事件侦听器。

  • 括号内是完成命令的两个参数。第一个参数是 Solar2D 将侦听的**事件类型**,在本例中为 "tap"(点击)。第二个参数是在事件发生时应该运行(调用)的**函数**,在本例中是我们上一节编写的 pushBalloon() 函数。本质上,我们告诉 Solar2D 每当用户点击气球时就运行 pushBalloon() 函数。

这就是全部——你现在拥有一个功能齐全的游戏了!如果你保存 main.lua 文件并重新启动模拟器,它应该就可以运行了。尽力继续点击气球并防止它碰到平台!

这是完整的程序,以防你遗漏了什么

-----------------------------------------------------------------------------------------
--
-- main.lua
--
-----------------------------------------------------------------------------------------

local background = display.newImageRect( "background.png", 360, 570 )
background.x = display.contentCenterX
background.y = display.contentCenterY

local platform = display.newImageRect( "platform.png", 300, 50 )
platform.x = display.contentCenterX
platform.y = display.contentHeight-25

local balloon = display.newImageRect( "balloon.png", 112, 112 )
balloon.x = display.contentCenterX
balloon.y = display.contentCenterY
balloon.alpha = 0.8

local physics = require( "physics" )
physics.start()

physics.addBody( platform, "static" )
physics.addBody( balloon, "dynamic", { radius=50, bounce=0.3 } )

local function pushBalloon()
    balloon:applyLinearImpulse( 0, -0.75, balloon.x, balloon.y )
end

balloon:addEventListener( "tap", pushBalloon )

额外练习

恭喜你,你只用了 30 行代码就创建了一个基本的游戏!但是还缺少一些东西,不是吗?如果游戏能够记录气球被点击了多少次,那岂不是很好?幸运的是,这很容易添加!

首先,让我们创建一个局部 Lua 变量来跟踪点击次数。你可以将它添加到现有代码的最顶部。在这种情况下,我们将使用它来存储一个**整数**,而不是将它与图像关联。由于玩家应该以零分开始游戏,我们最初将其值设置为 0,但这以后可以更改。

local tapCount = 0

local background = display.newImageRect( "background.png", 360, 570 )
background.x = display.contentCenterX
background.y = display.contentCenterY

接下来,让我们创建一个可视化对象来显示气球上的点击次数。还记得本章前面讨论的图层规则吗?新对象将放置在先前加载的其他对象的前面,因此该对象应该在加载背景**之后**加载(否则它将被放置在背景后面,你将看不到它)。

在加载/定位背景的三行代码之后,添加以下突出显示的命令

local tapCount = 0

local background = display.newImageRect( "background.png", 360, 570 )
background.x = display.contentCenterX
background.y = display.contentCenterY

local tapText = display.newText( tapCount, display.contentCenterX, 20, native.systemFont, 40 )

让我们更详细地检查一下这个命令

后两个参数 display.contentCenterX20 用于将此文本对象定位在屏幕上。你会注意到我们使用相同的快捷方式 display.contentCenterX 将对象定位在屏幕的水平中心,并使用 20 将其垂直 **y** 位置设置在屏幕顶部附近。

此 API 的第四个参数是渲染文本的字体。Solar2D 在所有平台上都支持自定义字体,但在这个游戏中,我们将使用默认系统字体,并指定为 `native.systemFont`。

最后一个参数(`40`)是渲染文本的预期大小

让我们检查一下这段新代码的结果。保存修改后的 `main.lua` 文件并重新启动模拟器。如果一切顺利,文本对象现在应该会显示在屏幕顶部附近。

继续我们的程序——默认情况下,使用 `display.newText()` 创建的文本将是白色的。幸运的是,这很容易更改。在你刚刚添加的代码行之后,输入高亮显示的命令

local tapText = display.newText( tapCount, display.contentCenterX, 20, native.systemFont, 40 )
tapText:setFillColor( 0, 0, 0 )

简单地说,这个 `setFillColor()` 命令修改了对象 `tapText` 的填充颜色。`setFillColor()` 命令最多接受四个数值参数,范围为`0` 到 `1`,分别对应于红色绿色蓝色alpha颜色通道。在这个游戏中,我们用纯黑色填充对象,所以前三个通道设置为 `0`(alpha 默认值为 `1`,在本例中可以省略)。

让我们继续!新的文本对象看起来不错,但实际上并没有做任何事情。为了让它在玩家点击气球时更新,我们需要修改我们的 `pushBalloon()` 函数。在这个函数中,在 `balloon:applyLinearImpulse()` 命令之后,插入两行高亮显示的代码

local function pushBalloon()
    balloon:applyLinearImpulse( 0, -0.75, balloon.x, balloon.y )
    tapCount = tapCount + 1
    tapText.text = tapCount
end

让我们逐行检查这些代码

仔细观察——要更新屏幕上的文本,我们更新的是文本对象的属性,而不是对象本身。在本例中,我们通过编写 `tapText.text` 来修改 `tapText` 的 `text` 属性,后跟 `=`,然后是它的新值。因为我们在前一行增加了 `tapCount` 变量的值,然后用相同的变量值更新文本对象,所以视觉显示将始终反映内部 `tapCount` 值。

就是这样!如果你保存 `main.lua` 文件并重新启动模拟器,你的游戏基本上就完成了——现在,每次你点击气球,屏幕顶部的计数器都会增加 1,有效地记录分数。

完整的 `BalloonTap` 程序可以在这里下载。如果你的项目没有按预期工作,请下载此源代码并将其与你创建的代码进行比较。

章节概念

我们在本章中涵盖了许多概念。这可能看起来有点 overwhelming,但请耐心,查看你的代码,并在必要时重新阅读这些章节。如果你需要帮助,Discord论坛是与其他开发者交流的友好场所。

以下是本章所学内容的快速概述

命令/属性 描述
`display.newImageRect()` 在屏幕上加载并显示图像。
`object.x` 设置对象的水平 x 位置。
`object.y` 设置对象的垂直 y 位置。
`display.contentCenterX` 沿 x 轴内容区域中心的快捷方式。
`display.contentCenterY` 沿 y 轴内容区域中心的快捷方式。
`object.alpha` 设置对象的 alpha 级别(不透明度/透明度)。
`require()` 加载给定的模块或库,例如 `"physics"`。
`physics.start()` 启动物理引擎。
`physics.addBody()` 将物理实体添加到对象。
`object:applyLinearImpulse()` 将模拟的冲量力应用于物理对象。
`object:addEventListener()` 向对象添加事件侦听器,例如 `"tap"`。
`display.newText()` 在屏幕上创建文本对象。
`object:setFillColor()` 设置文本和矢量对象的填充颜色。