In Delphi Objects are still available but are only meant for backward compatibility and their use is therefore not recommended.
From Delphi documentation:
Object types are supported for backward compatibility only. Their use is not recommended.
Perhaps you might consider using what I call a "fake class approach". In this approach you are actually storing your data in array of records but instead of accessing that data directly from records you are accessing them with properties of a standardized class.
Using this approach you have all of your data tightly stored in continuous memory blocks but still use advantages of classes. Here is a quick code example:
Code:
type
TDirection = (dirLeft, dirRight);
//Record definition for storing data of a sprite
RSprite = record
XPos: Single;
YPos: Single;
Width: Integer;
Height: Integer;
ImageIndex: Integer;
end;
//Array of sperite records to keep data stored in one continoud block of memory
ASprites = Array of RSprite;
//You can even split your data to multiple arrays keeping specific kinds of data
//like position, helth status, etc.
//This means that if for instance you need to loop through all characters to see
//if any of them should be dead (zero or negative health) only data related to
//health would be put on stack
RHealthStatus = record
HealthPoints: Integer;
MaxHealthPoints: Integer;
RegenerationRate: Integer;
end;
//Array of records containing unit health data
AHelthStatus = Array of RHealthStatus;
//Specialized class for accessing data.
//Note that all properties are indexed properties since you need to tell which
//sprite are you acessing
TSprites = class(TObject)
private
FSprites: ASprites;
protected
function GetSpritesCount: Integer;
function GetXPos(Index: Integer): Single;
function GetYPos(Index: Integer): Single;
function GetWidth(Index: Integer): Integer;
function GetHeight(Index: Integer): Integer;
function GetImageIndex(Index: Integer): Integer;
procedure SetXPos(Index: Integer; const AXPos: Single);
procedure SetYPos(Index: Integer; const AYPos: Single);
procedure SetWidth(Index: Integer; const AWidth: Integer);
procedure SetHeight(Index: Integer; const AHeight: Integer);
procedure SetImageIndex(Index: Integer; const AImageIndex: Integer);
public
procedure Add(ASprite: RSprite);
procedure Remove(SpriteIndex: Integer);
constructor Create(AInitialSize: Integer);
property SpritesCount: Integer read GetSpritesCount;
property XPos[Index: Integer]: Single read GetXPos write SetXPos;
property YPos[Index: Integer]: Single read GetYPos write SetYPos;
property Width[Index: Integer]: Integer read GetWidth write SetWidth;
property Height[Index: Integer]: Integer read GetHeight write SetHeight;
property ImageIndex[Index: Integer]: Integer read GetImageIndex write SetImageIndex;
end;
//You can even subclass the base sprite class for adding additional functionality
TMovableSprite = class(TSprites)
protected
function Move(Distance: Integer; Direction: TDirection): Boolean;
//Inherited properties
property SpritesCount;
property XPos;
property YPos;
property Width;
property Height;
property ImageIndex;
end;
//You can even acces data from different specialized arrays of records
TPlayer = class(TMovableSprite)
private
FHealthStatus: RHealthStatus;
protected
function GetHealthPoints: Integer;
function GetMaxHealthPoints: Integer;
function GetRegenerationRate: Integer;
procedure SetHealthPoints(const AHealthPoints: Integer);
procedure SetMaxHealthPoints(const AMaxHealthPoints: Integer);
procedure SetRegenerationRate(const ARegenerationRate: Integer);
public
property HealthPoints: Integer read GetHealthPoints write SetHealthPoints;
property MaxHealthPoints: Integer read GetMaxHealthPoints write SetMaxHealthPoints;
property RegenarationRate: Integer read GetRegenerationRate write SetRegenerationRate;
//Inherited properties
property SpritesCount;
property XPos;
property YPos;
property Width;
property Height;
property ImageIndex;
end;
implementation
{ TSprites }
procedure TSprites.Add(ASprite: RSprite);
begin
SetLength(FSprites,Length(FSprites)+1);
end;
constructor TSprites.Create(AInitialSize: Integer);
begin
SetLength(FSprites, AInitialSize);
end;
function TSprites.GetHeight(Index: Integer): Integer;
begin
result := FSprites[Index].Height;
end;
function TSprites.GetImageIndex(Index: Integer): Integer;
begin
result := FSprites[Index].ImageIndex;
end;
function TSprites.GetSpritesCount: Integer;
begin
result := Length(FSprites);
end;
function TSprites.GetWidth(Index: Integer): Integer;
begin
result := FSprites[Index].Width;
end;
function TSprites.GetXPos(Index: Integer): Single;
begin
result := FSprites[Index].XPos;
end;
function TSprites.GetYPos(Index: Integer): Single;
begin
result := FSprites[Index].YPos;
end;
procedure TSprites.Remove(SpriteIndex: Integer);
begin
//code for removing object from array
end;
procedure TSprites.SetHeight(Index: Integer; const AHeight: Integer);
begin
FSprites[Index].Height := AHeight;
end;
procedure TSprites.SetImageIndex(Index: Integer; const AImageIndex: Integer);
begin
FSprites[Index].ImageIndex := AImageIndex;
end;
procedure TSprites.SetWidth(Index: Integer; const AWidth: Integer);
begin
FSprites[Index].Width := AWidth;
end;
procedure TSprites.SetXPos(Index: Integer; const AXPos: Single);
begin
FSprites[Index].XPos := AXPos;
end;
procedure TSprites.SetYPos(Index: Integer; const AYPos: Single);
begin
FSprites[Index].YPos := AYPos;
end;
{ TMovableSprite }
function TMovableSprite.Move(Distance: Integer; Direction: TDirection): Boolean;
begin
//Code for moving sprites
end;
{ TPlayer }
function TPlayer.GetHealthPoints: Integer;
begin
result := FHealthStatus.HealthPoints;
end;
function TPlayer.GetMaxHealthPoints: Integer;
begin
result := FHealthStatus.MaxHealthPoints;
end;
function TPlayer.GetRegenerationRate: Integer;
begin
result := FHealthStatus.Regenerationrate;
end;
procedure TPlayer.SetHealthPoints(const AHealthPoints: Integer);
begin
if AHealthPoints > FHealthStatus.MaxHealthPoints then
FHealthStatus.HealthPoints := FHealthStatus.MaxHealthPoints
else if AHealthPoints < 0 then
FHealthStatus.HealthPoints := 0
else
FHealthStatus.HealthPoints := AHealthPoints;
end;
procedure TPlayer.SetMaxHealthPoints(const AMaxHealthPoints: Integer);
begin
FHealthStatus.MaxHealthPoints := AMaxHealthPoints;
end;
procedure TPlayer.SetRegenerationRate(const ARegenerationRate: Integer);
begin
FHealthStatus.Regenerationrate := ARegenerationRate;
end;
If you decide to go for splitting data into multiple arrays you need to make sure all arrays have same order of items (common index).
This approach comes most useful when you are creating and destroying objects quote often since you don't suffer from class creation/destruction overhead.
Another or its advantages is that because you are storing your data in arrays which are continuous memory blocks you can save them to or load them from files as such instead of needing to loop through a bunch of classes and save/load data for each of them separately.
Notable drawbacks is the fact that if you store all the data in a single array the records that must be of the same type must have all the fields for all the data that most complex object stored in that array would need. This would mean that you might be wasting some space with more simpler objects whose data would not fill all the records fields.
And if you go for multiple arrays approach maintaining data in them becomes more difficult.
And probably the biggest drawback of such approach would be hard way to achieving good data reusability, even thou it is still doable.
If you need to discus some more details of this design please let me know.
Bookmarks