团队编程

在 Solar2D 中,显示组 是以层次结构组织对象的标准方式。

概述

理解 显示组 对于在 Solar2D 中构建应用程序至关重要。显示组是一种特殊的 显示对象,它可以包含其他显示对象,甚至其他显示组。可以将其想象成一张空白纸,您可以在上面“绘制”图像、文本、形状和动画精灵。

在 Solar2D 中进行应用程序开发时,需要注意的是,显示组不受屏幕边界的限制。实际上,显示组是无限的,并且在所有方向上都延伸到无穷大 — 物理屏幕边缘只是 舞台 的一部分。在 Solar2D 中,舞台可以被认为是父显示组,您创建的新显示组会自动添加为它的子组。

坐标系

所有显示组,包括 舞台,都围绕一个坐标系旋转。您放置在屏幕上的任何可视对象都将具有一个 xy 坐标,分别指示其水平和垂直位置。

在 Solar2D 中,显示组的默认原点是 0,0,它位于舞台的左上角,而不是中心。从这个 0,0 点开始,正 x 值向右延伸,而正 y 值向下延伸(与 笛卡尔 坐标系中的向上延伸不同)。也允许使用负坐标,但如下面的图表所示,它们不会位于屏幕上可见的组部分(黑色区域)内。但是,可以移动组以调整其原点 — 有关更多信息,请参见下面的 组变换

绘制模型

“空白纸”的比喻有助于理解 Solar2D 的绘制模型。就像您在纸上绘制图形一样,放置在显示组中的 Solar2D 显示对象 将成为该组(纸张)的一部分。

未放置到特定组中的显示对象将成为 舞台 的一部分,但在开发应用程序的过程中,您通常会以特定的分层顺序创建多个显示组。您可以将其想象成“一堆纸”,每个显示组代表堆栈中的一张纸。例如,假设您要模拟具有以下三层(三个显示组)的“风景画”:

  1. 远景背景 — 包含天空、云彩等。
  2. 近景背景 — 包含背景山脉
  3. 前景 — 包含树木、草地、岩石等。

这些显示组中的每一个都将依次包含适当的显示对象,从而以正确的分层顺序构成绘画。当然,虽然每一层在比喻中都代表一张纸,但您实际上看不到“纸”本身 — 只需将它们想象成透明的纸张,您可以在上面放置文本、图像等。

创建组

创建新的显示组非常简单,如下所示:

local myGroup = display.newGroup()

这将创建一个新的显示组,并将其分配给局部变量 myGroup。然后,您可以向其添加显示对象

local myGroup = display.newGroup()

--Create a rectangle
local rect = display.newRect( 0, 0, 40, 40 )

--Insert it into 'myGroup'
myGroup:insert( rect )

许多显示对象 API 也接受组作为内联参数。有关特定 API的链接,请参阅 显示对象 指南。

local myGroup = display.newGroup()

--Create a rectangle and insert it into 'myGroup'
local rect = display.newRect( myGroup, 0, 0, 40, 40 )

组层级

您应该以特定顺序创建显示组。就您的代码而言,**首先**声明的组实际上将位于视觉分层顺序中**下一个**组的**后面**。例如,要创建上面讨论的“风景画”,请按如下方式声明三个显示组

local farBackground = display.newGroup()  
local nearBackground = display.newGroup()  --this will overlay 'farBackground'  
local foreground = display.newGroup()  --and this will overlay 'nearBackground'
重要

尽管显示组本质上是表,但 Lua 库函数(如 table.insert()ipairs())与组**不兼容**。此外,您无法使用 #myGroup 获取显示组中子对象的數量。请改用 myGroup.numChildren

移除组

可以通过 display.remove()object:removeSelf() 函数移除组。执行此操作时,组中的所有子对象也将被移除。但是,您仍然必须手动移除与这些子对象关联的变量或其他引用,否则它们将不会从内存中释放。有关更多详细信息,请参阅 显示对象 指南。

local myGroup = display.newGroup()

--Create 2 rectangles and insert them into 'myGroup'
local rect1 = display.newRect( myGroup, 0, 0, 40, 40 )
local rect2 = display.newRect( myGroup, 30, 30, 60, 60 )

myGroup:removeSelf()
--OR
display.remove( myGroup )

myGroup = nil

子对象属性

当您修改组的属性时,它的所有子对象都会受到影响。例如,如果您在显示组上设置 alpha 属性,则每个子对象的 alpha 值将乘以组的新 alpha 值。组会自动检测子对象的属性何时发生更改(位置、旋转等)。因此,在下一个渲染过程中,子对象将重新渲染。

组变换

组变换以层次结构方式应用于组的子对象。首先应用子对象的变换,然后应用其父对象的变换,然后应用每个祖先的变换,一直到 舞台。因此,组中的子对象是相对于父组的位置进行定位的。当您修改组的变换(移动/缩放/旋转)时,它会影响子对象的变换。

在下图中,黑色区域表示理论上的 Solar2D 内容区域(横向),橙色线条的交点表示组的原点。请注意,此原点始终默认为 0,0(内容区域的左上角)。

为了进行测试,在 100,100 处绘制了一个红色矢量对象,这表示其中心位置,因为 显示对象 具有默认的中心 锚点

local myGroup = display.newGroup()

local myBox = display.newRect( 100, 100, 80, 80 )
myBox:setFillColor( 1, 0, 0, 0.8 )
myGroup:insert( myBox )

如果将组移动(变换)到新的 xy 位置,则其原点将移动到该点

myGroup.x = 50
myGroup.y = 50

请注意,红色框会随着组(其父对象)一起移动。但是,对象的**固有** xy 位置**不会**更改 — 即使该框显示在**内容**位置 150,150 处,它仍然保持在 100,100 处。这是因为 Solar2D 管理显示对象相对于其父组的位置。

要获取对象在内容坐标中的实际位置,而不考虑移动/缩放/旋转的组,请使用 object:localToContent() 函数

local actualBoxX, actualBoxY = myBox:localToContent( 0,0 )
print( actualBoxX, actualBoxY )

组锚点

对于普通的 显示对象(例如图像或矢量形状),其 锚点 控制其几何图形相对于其**原点**的定位方式。当您更改普通显示对象的锚点时,其原点**不会**更改。相反,它的几何图形会相对于其原点移动。有关更多信息,请参阅 变换和锚点 指南。

相反,显示组 默认情况下不考虑锚点。这是因为显示组,根据基本原则,是无限的,并且在所有方向上都延伸到无穷大。但是,它们确实具有一个默认为 0,0xy 位置(内容区域的左上角(内容区域的左上角)。此位置是组的原点。

或者,可以在显示组上使用锚点。为了演示此方法,应将另一个显示对象添加到 组变换 部分中概述的示例中

local myGroup = display.newGroup()

local myBox = display.newRect( 100, 100, 80, 80 )
myBox:setFillColor( 1, 0, 0, 0.8 )
myGroup:insert( myBox )

myGroup.x = 50
myGroup.y = 50

--Add a smaller blue box
local blueBox = display.newRect( 50, 50, 50, 50 )
blueBox:setFillColor( 0, 0, 1, 0.8 )
myGroup:insert( blueBox )

现在,将组的锚点设置为左上角(anchorXanchorY 均设置为 0

myGroup.anchorX = 0
myGroup.anchorY = 0

如果刷新项目,则不会出现**视觉**变化,因为默认情况下,显示组不考虑锚点。但是,您可以通过将显示组的 anchorChildren 属性设置为 true 来实现锚点行为。

在探讨 anchorChildren 属性的工作原理之前,请检查下图中的理论边界框(不透明白色)— 此框将用作理解组上锚点的可视辅助。请注意,边界框会考虑组中**所有**显示对象的最大顶部、右侧、底部和左侧点

现在,将 anchorChildren 属性设置为 true

myGroup.anchorChildren = true

请注意,整个组边界将移动到组的**当前原点**(如前所述,为 50,50)。因为组的锚点设置为 0,0(左上角),所以其边界的左上角(而不是其中心)位于组的原点。

如果整个组原点移动,则对象将按预期移动

myGroup.x = 150

如果将锚点重置为右上角 (1,0),则该组将在当前原点 150,50 处重新定位

myGroup.anchorX = 1
myGroup.anchorY = 0

本质上,当显示组的 anchorChildren 属性设置为 true 时,所有子对象都将偏移相同的量。该偏移量是根据组中所有子对象的边界框计算的。实际上,子对象被视为一个单元,组的原点是该单元的锚点。

注意

使用 anchorChildren 的替代方法是将组放置在另一个组(“父组”)中。更改此父组的位置、旋转或缩放将产生类似的行为。

容器

一种称为 容器 的特殊类型的组可以将组的边界限制为预定义区域。这将有效地将容器的子对象剪切到动态矩形区域内。有关更多信息,请参阅 使用容器 指南。

屏幕外剔除

Solar2D 将剔除屏幕边界之外的子对象。