0

我想创建一个我TSprite在运行时调用的记录。

TSprite是我正在构建的关卡编辑器中使用的图像和 8 个选择点。

type 
  TSprite = record
    Image: TImage;
    Selection: TSelection;
    SelectionPointTL: TSelectionPoint; // top-left
    SelectionPointTM: TSelectionPoint; // top-middle
    SelectionPointTR: TSelectionPoint; // top-right
    SelectionPointML: TSelectionPoint; // middle-left
    SelectionPointMR: TSelectionPoint; // middle-right
    SelectionPointBL: TSelectionPoint; // bottom-left
    SelectionPointBM: TSelectionPoint; // bottom-middle
    SelectionPointBR: TSelectionPoint; // bottom-right
  end;

现在我想将它存储在一个数组中。

arrSprites: array[0..1000] of TSprite;

现在创作(我正在努力的部分)

这是我到目前为止所拥有的:

arrSprites[i].Image.Position.X := frmMainUI.CurrentMouseX;
arrSprites[i].Image.Position.Y := frmMainUI.CurrentMouseY;
arrSprites[i].Image.Bitmap.LoadFromFile('1.png');
arrSprites[i].Image.Visible := True;
arrSprites[i].Image.WrapMode := TImageWrapMode.iwStretch;

所以这段代码应该做的是在一个滚动框内创建一个带有选择的图像:fsbcanvas。

为了清楚起见,我要求提供创建TSprite.

谢谢

4

3 回答 3

9

不需要创建记录。它们是值类型,您应该像考虑其他值类型一样看待它们,例如Integer. 声明一个作为值类型的局部变量或类字段,这就是您需要做的所有事情。同样,一个常量大小的数组是一个值类型。

因此,您的问题的答案是arrSprites不需要任何特殊分配。需要分配和初始化的是你记录的内容。因此,如果您的记录中的任何字段是类实例,那么它们需要实例化。因此,请考虑以下记录:

type
  TMyRecord = record
    i: Integer;
    obj: TObject;
  end;

你可以这样声明:

var
  rec: TMyRecord;

并分配记录本身。但是你需要初始化它的成员:

rec.i := 42; // or some other initial value
rec.obj := TObject.Create; // instantiate the object

当您完成记录后,您需要销毁该对象。

rec.obj.Free;

这很容易出错,因此通常您的记录应该只包含值类型或托管类型(例如字符串、接口、动态数组等)

现在,我无法从问题中看出您的代码是什么,但我怀疑您的记录有一些类实例。这立即使记录成为数据结构的可疑选择。我会将它们保存在一个类中,该类具有管理对象生命周期的构造函数和析构函数。

而且我也会避免使用恒定长度的数组。他们非常不灵活。相反,我建议您将精灵对象保存在通用列表中,TList<T>. 或者,也许更好,TObjectList<T>这样您就可以让列表照顾其成员的一生。

于 2013-02-21T15:32:42.017 回答
9

我正在输入一个答案,因为 David Heffernan 刚刚击败了我。在此期间,我想补充一下;

意识到在您的第一个代码示例中,您正在使用记录 (TSprite) 来保存对象 (TImage)。TImage 实际上是一个可视化组件,但最终它是从 TObject 派生出来的。

记录与大多数变量(如整数)一样,不必实例化,可以随意复制。

然而,对象是指向实例的指针,必须按照以下步骤创建/销毁

rec.Image := TImage.Create(nil);
// do other things..
rec.Image.Free;

因此,如果操作不当,可能会导致内存泄漏或访问冲突错误。(例如,在复制 TSprite 时..)在这个设置中有很多可能出错的地方,所以我说;

在记录中使用对象可能会很棘手。考虑将对象保存在对象中:

一个简单的解决方案(如果您确实想在 TSprite 中保留 TImage 的实例或指针引用)是使 TSprite 也成为一个对象。它可以通过使用其构造函数和析构函数来自动跟踪创建/销毁:

TSpriteObject = class(TObject)
public
  Image : TImage;

  constructor Create;
  destructor  Destroy; override;
end;

及其实现:

constructor TSpriteObject.Create;
begin
  Image := TImage.Create(nil);
  // ^ TImage is a component and expects an Owner component that would also
  //   destroy it, so we use a nil value to disable that behavior.
end;

destructor TSpriteObject.Destroy;
begin
  Image.Free;
end;

然后,您可以让 TObjectList 跟踪许多 TSprite 实例,并且当列表被清除或销毁时,它将销毁任何 TSprite(这会破坏其 TImage)。

(这是我第一次尝试stackoverflow帖子,请耐心等待我在这里发现我做错了什么)

于 2013-02-21T15:51:57.057 回答
1

正如 David Heffernan 所说,对于这种情况,类可能是更好的数据结构。但是,如果您决定改用记录,则可以在记录中声明方法来重构实例化和销毁部分:

type
  TSprite = record
    Image: TImage;
    Selection: TSelection;
    SelectionPointTL: TSelectionPoint; // top-left
    SelectionPointTM: TSelectionPoint; // top-middle
    SelectionPointTR: TSelectionPoint; // top-right
    SelectionPointML: TSelectionPoint; // middle-left
    SelectionPointMR: TSelectionPoint; // middle-right
    SelectionPointBL: TSelectionPoint; // bottom-left
    SelectionPointBM: TSelectionPoint; // bottom-middle
    SelectionPointBR: TSelectionPoint; // bottom-right
    procedure Create(aImageOwner: TComponent);
    procedure Destroy;
  end;

{ TSprite }

procedure TSprite.Create(aImageOwner: TComponent);
begin
  Image := TImage.Create(aImageOwner);
end;

procedure TSprite.Destroy;

begin
  Image.Free;
end;

// ...

var
  Rec: TSprite;
begin
  Rec.Create(Form1);

  // ...

  Rec.Destroy;
end;

请注意,它不是真正的类实例化,因此Rec := TSprite.Create(Form1);没有意义,除非您想以TSprite.Create这种方式定义和实现。

于 2013-02-22T09:43:23.243 回答