Can you move multiple images with one timer? Of course you can. You just need a mechanism to iterate over them.
Since you are using components one bad way would be to iterate through all Controls of your frmTank
and check if their name starts with img
. But this is bad approach as you would be iterating through all controls that are on your form and doing lots of string comparisons.
The good way of doing this would be to create a list or an array that would contain references to your images so you can easily loop thought them.
Since you are new in coding i would recommend using List at this time so you don't have to deal with additional code that would be required to manage dynamic array allocation when adding items and resizing and reordering of items in an array when removing items.
So you should create yourself a List to store references to the mines
procedure TForm3.FormCreate(Sender: TObject);
begin
//Create a list for storing references to your LandMine images
LandMines := TList.Create;
end;
Then while you are creating the LandMines you also add them to the said list
imgLandmine := TImage.Create(frmTank);
//Add your LandMine image to the list
LandMines.Add(imgLandMine);
And finally in your OnTimer event you can then loop through all the LandMines using
procedure TForm3.Timer1Timer(Sender: TObject);
var I: Integer;
//Just local TImage variable to store reference to current landmine we will be
//working with for easier code readability
LandMine: TImage;
begin
//Loop through the LandMines list
//NOTE: Since list is 0 based you should pay attention that your loop does not
//exceed list bounds. So always use List.Count-1 end loop position
for I := 0 to LandMines.Count-1 do
begin
//Retrieve reference to specific landmine image from the list and set the
//local LandMine variable to it for easier code readability
LandMine := TImage(LandMines[I]);
//Update the landmine position
LandMine.Left := Landmine.Left - iMineSpeed;
//Check to see if the LandMine is out of viewable area so it can be destroyed
//
//Checking mine position after moving it to new position saves the need to use
//if..else if clause that you used in your code example which can improve in
//code readability
if (LandMine.Left + LandMine.Width) < 0 then //Destroy landmine
end;
end;
NOTE: instead of destroying your mines after they leave the viewable area you might want to just move them back to the right side changing their height position to some random value.This way you avoid having to constantly create and destroy image components which does have some performance overhead. And to avoid the feeling of mines coming at constant rate just add some random value to their left position when you reallocate them which would mean that they would come with some random delay into viewable are from the right side.
Having all the LandMines in a list will also allow you later to easily loop thought them and check their collision with your tank.
You might also want to create a similar list for your and possibly enemy projectiles. I'm guessing your game would also have these.
PS: If you want your projectiles to have different speeds you could store speed for each projectile using TImage.Tag property which is an inherited property from TComponent and allows you to store simple Integer value. And then when you are updating the position of your projectiles you read speed of each projectile from the mentioned Tag property instead from some global constant.
Heck you could even update the projectile speed in each cycle to simulate air drag affecting them if you want. Would require some clever math so that speed would not slow down too fast since Tag
property is just an integer and not a Floating point value (storing initial speed multiplied by lets us say 100 and then dividing this stored speed by 100 every time to get needed movement speed without the use of Floating point values).
Anyway lists are a good way of grouping objects together into some logical collections. And yes you can have one object in multiple lists if needed since lists don't really store the objects themselves but only references to the said objects.
Just don't forget to remove your objects from the list(s) before destroying them or you will end up with your list referencing the nonexistent objects and thus running into Access Violations
when trying to access such objects.
You can remove objects from list by calling List.Delete
This is easy when you are looping through a list since you already have an Index of a current item. But what to do when you don't have an index of an item but only reference to the said item? Then you use List.IndexOf method to retrieve the index of specific object in the list.
Delphi also has a TObjectList which is similar to a TList except that it can also Own the objects that are added to it.
What does this means? This means that if TObjectList
is set to own the object and you Delete an object from it it will automatically notify the said object that it needs to be destroyed. But the disadvantage is that only one list can own an object at the same time. So if you are adding your object to multiple list one list may Destroy a specific object without notifying other list that the said object has been destroyed so they would reference to a nonexistent object.