Recent

Author Topic: Image inside Bitmap  (Read 3826 times)

LeopardCoder

  • New member
  • *
  • Posts: 8
Image inside Bitmap
« on: January 22, 2023, 02:45:25 pm »
I am currently programming a game in Lazarus and would like to display the image of my player. My player is a record and so far has values like health and damage as elements in the record. But to check for collisions I need a bitmap (is that right). So I tried to load the image into a bitmap somehow, but it didn't work. I hope you can help me with this.
So I am looking for the code:
Having a bitmap that displays an image with my player that is the same size as the image itself.

I would really appreciate any replies.

PS: I am fairly new to Lazarus/Pascal.

With kind regards :D

furious programming

  • Hero Member
  • *****
  • Posts: 853
Re: Image inside Bitmap
« Reply #1 on: January 22, 2023, 07:19:39 pm »
But to check for collisions I need a bitmap (is that right).

I you need to test collisions, you need hitboxes, not images. Collisions are not performed per pixel, because firstly, it is inefficient, and secondly, the end result is annoying and makes it difficult for players to play. Try using simple geometric figures to describe collision-sensitive space — hitboxes in a 2D world are usually rectangles or circles for simplicity of calculation (one or many per object).

If you're making a game using visual components from the LCL library, it's best to throw the code in the trash right away and start over — this time using a sensible game development library, such as SDL, Allegro, Raylib, etc. Playing with LCL is a waste of time.
Lazarus 3.2 with FPC 3.2.2, Windows 10 — all 64-bit

Working solo on an acrade, action/adventure game in retro style (pixelart), programming the engine and shell from scratch, using Free Pascal and SDL. Release planned in 2026.

LeopardCoder

  • New member
  • *
  • Posts: 8
Re: Image inside Bitmap
« Reply #2 on: January 22, 2023, 08:40:58 pm »
Thank you very much for your answer. Unfortunately, this project has to be done in Lazarus without a sensible game development library. I would now give each object a shape (rectangle) and use Left and Top to check if it collides with another shape in the form. Is the following code suitable:
Code: Pascal  [Select][+][-]
  1. var
  2. shape1: TShape;
  3. i: Integer;
  4. begin
  5. shape1 := TShape.Create(Self);
  6.  
  7. // Assign shape1 properties
  8. shape1.Shape := stRectangle;
  9. shape1.Width := 100;
  10. shape1.Height := 50;
  11. shape1.Left := 50;
  12. shape1.Top := 50;
  13.  
  14. // Iterate through all shapes on the form
  15. for i := 0 to Self.ComponentCount - 1 do
  16. begin
  17. if Self.Components[i] is TShape then
  18. begin
  19. // Check for collision with shape1
  20. if (shape1.Left + shape1.Width >= TShape(Self.Components[i]).Left) and (shape1.Left <= TShape(Self.Components[i]).Left + TShape(Self.Components[i]).Width) and
  21. (shape1.Top + shape1.Height >= TShape(Self.Components[i]).Top) and (shape1.Top <= TShape(Self.Components[i]).Top + TShape(Self.Components[i]).Height) then
  22. begin
  23. ShowMessage('Collision detected with shape ' + TShape(Self.Components[i]).Name + '!');
  24. end;
  25. end;
  26. end;
  27.  

furious programming

  • Hero Member
  • *****
  • Posts: 853
Re: Image inside Bitmap
« Reply #3 on: January 22, 2023, 09:28:32 pm »
Unfortunately, this project has to be done in Lazarus without a sensible game development library.

Could you tell me what is the reason for that?

Also, using a library like SDL doesn't mean you have to use a foreign engine. Absolutely — SDL is just a set of functions for different things, and you'll have to create all the data structures and all the logic yourself anyway. The difference, however, is that SDL contains a lot of functionality, including convenient rendering (including using the GPU), support for a sound mixer (as many sounds played simultaneously as you want), support for input devices, etc., so you can create any program (not just games) and use your hardware to the full, using just simple functions.

I also used to make a game using only classes from LCL (check out the Deep Platformer game prototype) and the performance of this solution is very poor. So it's a waste of time for such games, it's better to spend it on a sensible API. Take this as good advice and save yourself time.



Answering your question, you are making few mistakes. First, this code:

Code: Pascal  [Select][+][-]
  1. for i := 0 to Self.ComponentCount - 1 do
  2. begin
  3.   if Self.Components[i] is TShape then
  4.   begin
  5.     // Check for collision with shape1
  6.     if (shape1.Left + shape1.Width >= TShape(Self.Components[i]).Left) and (shape1.Left <= TShape(Self.Components[i]).Left + TShape(Self.Components[i]).Width) and
  7.        (shape1.Top + shape1.Height >= TShape(Self.Components[i]).Top) and (shape1.Top <= TShape(Self.Components[i]).Top + TShape(Self.Components[i]).Height) then
  8.     begin
  9.       {...}

is buggy and horribly inefficient. Not only does it test collisions with all objects on the form (including itself, which is a bug), but it also does not cache references, so each expression in the condition means extracting the object of the tested control from the form's components list.

Code: Pascal  [Select][+][-]
  1. var
  2.   NewShape:   TShape;
  3.   Shape:      TShape;
  4.   Index:      Integer;
  5.   CommonPart: TRect;
  6. begin
  7.   NewShape        := TShape.Create(Self);
  8.   NewShape.Parent := Self;
  9.   NewShape.Shape  := stRectangle;
  10.   NewShape.Width  := 100;
  11.   NewShape.Height := 50;
  12.   NewShape.Left   := 50;
  13.   NewShape.Top    := 50;
  14.  
  15.   // iterate through all components
  16.   for Index := 0 to Self.ComponentCount - 1 do
  17.     if Self.Components[Index] is TShape then
  18.     begin
  19.       Shape := Self.Components[Index] as TShape;
  20.  
  21.       // do not test collision with self
  22.       if Shape <> NewShape then
  23.         // check collision
  24.  

Secondly, if all the shapes are rectangles, instead of checking each edge of two rectangles manually, use a structure with its area, such as Shape.BoundsRect and for testing the intersection of rectangles there is already a function — IntersectRect from the Types module. So your code can be simplified:

Code: Pascal  [Select][+][-]
  1. var
  2.   NewShape:   TShape;
  3.   Shape:      TShape;
  4.   Index:      Integer;
  5.   CommonPart: TRect;
  6. begin
  7.   NewShape        := TShape.Create(Self);
  8.   NewShape.Parent := Self;
  9.   NewShape.Shape  := stRectangle;
  10.   NewShape.Width  := 100;
  11.   NewShape.Height := 50;
  12.   NewShape.Left   := 50;
  13.   NewShape.Top    := 50;
  14.  
  15.   // iterate through all components
  16.   for Index := 0 to Self.ComponentCount - 1 do
  17.     if Self.Components[Index] is TShape then
  18.     begin
  19.       Shape := Self.Components[Index] as TShape;
  20.  
  21.       // do not test collision with self
  22.       if Shape <> NewShape then
  23.         // check collision
  24.         if IntersectRect(CommonPart, Shape.BoundsRect, NewShape.BoundsRect) then
  25.         begin
  26.           // show message and use "CommonPart" or not
  27.           ShowMessage('Collision detected with ' + Shape.Name + '!');
  28.           Exit;
  29.         end;
  30.     end;
  31.  

This code can be further optimized, but using LCL classes to represent game objects and to render it on the screen is still a waste of CPU time, so who cares. 8)



Also remember that keeping all objects in one list is the worst possible solution, because every time you want to check if an object collides with another, you will iterate through the whole list. The complexity of such a solution is dire, basically O(n) where n is the number of objects in the list, and if you have m objects to move and detecting collisions, instead of iterating the list n times, you'll have to iterate the list n*m times. With 100 moving objects on the screen, checking their collisions will require 100*100 tests, or 10,000 — the increase is exponential.
« Last Edit: January 22, 2023, 09:46:49 pm by furious programming »
Lazarus 3.2 with FPC 3.2.2, Windows 10 — all 64-bit

Working solo on an acrade, action/adventure game in retro style (pixelart), programming the engine and shell from scratch, using Free Pascal and SDL. Release planned in 2026.

LeopardCoder

  • New member
  • *
  • Posts: 8
Re: Image inside Bitmap
« Reply #4 on: January 22, 2023, 10:48:59 pm »
Yes, of course. I have to do this project for school. We are supposed to program a game/app in Lazarus. I have decided to reprogram "Archero" in a simplified form: I am controlling the player with a timer that is activated every 10 milliseconds, so the player moves smoothly. In addition, there are about 5 enemies per level that move towards the player and then cause damage or shoot at the player. The player only shoots when he stops, and then always aims at the nearest enemy. The whole game is only implemented in 2D.
I could, however, ask again whether we can also use sensible game development libraries. There are no great expectations for the game itself.  However, I have already spent a lot of time doing all the calculations and everything myself. We only have a few weeks until the deadline. Do you think it's still worth switching to SDL? How long would it take to understand SDL well enough to use it?

Btw: When I use your Code, I get the Error: "Identifier not found "IntersectRect". Is there anything i need to add (in the uses for example)?
Here is the code I already have:
Code: Pascal  [Select][+][-]
  1. unit Unit1;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. interface
  6.  
  7. uses
  8.   Classes, SysUtils, Forms, Controls, Graphics, Dialogs, ExtCtrls, LCLtype, StdCtrls, Math;
  9.  
  10. //Player record
  11. type
  12.   TPlayer = record
  13.     Image:       TImage;
  14.     Health:      integer;
  15.     Damage:      integer;
  16.     FireRate:    integer;
  17.     Tag:         integer;
  18.   end;
  19. type
  20.   TArrow = record
  21.     Image:        TImage;
  22.     Damage:       integer;
  23.     Tag:          integer;
  24.   end;
  25.  
  26. //Enemy1 record
  27. type
  28.   TEnemy = record
  29.     Image:       TImage;
  30.     Health:      integer;
  31.     Damage:      integer;
  32.     Tag:         integer;
  33.   end;
  34. type
  35.  
  36.   { TForm1 }
  37.   TForm1 = class(TForm)
  38.   Timer1: TTimer;
  39.   MoveArrow: TTimer;
  40.   ShootArrow: TTimer;
  41. procedure MoveArrowTimer;
  42. procedure FormActivate(Sender: TObject);
  43. procedure FormKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
  44. procedure FormKeyUp(Sender: TObject; var Key: Word; Shift: TShiftState);
  45. procedure ShootArrowTimer(Sender: TObject);
  46. procedure Timer1Timer(Sender: TObject);
  47. procedure Upgrade1Click(Sender: TObject);
  48. procedure Upgrade2Click(Sender: TObject);
  49. procedure Upgrade3Click(Sender: TObject);
  50. procedure ShowUpgrades;
  51.  
  52.   private
  53.  
  54.   public
  55.  
  56.   end;
  57.  
  58. var
  59.   Form1                                  : TForm1;
  60.   Player                                 : TPlayer;
  61.   MoveUp, MoveDown, MoveLeft, MoveRight  : boolean;
  62.   ArrayEnemies                           : array of TEnemy;
  63.   //Legt fest, welcher Pfeil bewegt wird
  64.   PictureOfArrowToShoot                  : Timage;
  65.   //X und Y abstände zwischen Gegner und Gegner, der am nähesten ist
  66.   xDiff                                  : integer;
  67.   yDiff                                  : integer;
  68.   canShoot                               : boolean;
  69.   PlayerCollision                        : boolean;
  70.  
  71. const
  72.   //Anzahl an Gegnern im Level
  73.   AmountWilly = 5;
  74.   AmountKlaus = 5;
  75.   AmountFreddy = 5;
  76.  
  77. implementation
  78.  
  79. {$R *.lfm}
  80. //Beliebiges Bild laden
  81. procedure LoadImage(ImageToLoad: TImage; FileName: string);
  82. begin
  83.  
  84.   ImageToLoad.Picture.LoadFromFile(FileName);
  85.  
  86. end;
  87.  
  88. //Enemies Spawnen
  89.  
  90. procedure SpawnEnemies(const NumEnemies: Integer);
  91. var
  92.   k: Integer;
  93. begin
  94.  
  95.   SetLength(ArrayEnemies, NumEnemies);
  96.   for k := 0 to NumEnemies - 1 do
  97.   begin
  98.     ArrayEnemies[k].Image := TImage.Create(nil);
  99.     ArrayEnemies[k].Image.Parent := Form1;
  100.     ArrayEnemies[k].Image.Left := 50 + (k*10);
  101.     ArrayEnemies[k].Image.Top := 50 +(k*10);
  102.     ArrayEnemies[k].Image.Width := 100;
  103.     ArrayEnemies[k].Image.Height := 100;
  104.     ArrayEnemies[k].Image.Stretch := true;
  105.     LoadImage(ArrayEnemies[k].Image, 'Bilder/BulletTest.jpg');
  106.     ArrayEnemies[k].Health := 100;
  107.   end;
  108.  
  109. end;
  110.  
  111. //Position eines Enemys ändern
  112. procedure SetEnemyPosition(const EnemyIndex: Integer;  NewLeft, NewTop: Integer);
  113. begin
  114.  
  115.   ArrayEnemies[EnemyIndex].Image.Left := NewLeft;
  116.   ArrayEnemies[EnemyIndex].Image.Top := NewTop;
  117.  
  118. end;
  119.  
  120. procedure TForm1.FormActivate(Sender: TObject);
  121. var
  122. //Für Collision - weg machen
  123. X, Y, i: Integer;
  124. begin
  125.  
  126.   //Bild des zu schießenden Pfeil muss vorher erstmal erschaffen werden und dient solange als Platzhalter
  127.   PictureOfArrowToShoot := TImage.Create(nil);
  128.  
  129.   //Bild des Spieler Laden
  130.   Player.Image := TImage.Create(nil);
  131.   LoadImage(Player.Image, 'Bilder/UserTest.jpg');
  132.   Player.Image.Parent := Form1;
  133.   Player.Image.Left := 10;
  134.   Player.Image.Top := 10;
  135.   Player.Image.Width := 100;
  136.   Player.Image.Height := 100;
  137.   Player.Image.Stretch := true;
  138.   Player.FireRate := 1000;
  139.  
  140.   //Position des Spielers zu Beginn festlegen
  141.   Player.Image.Left := (Self.ClientWidth - Player.Image.Width) div 2;
  142.   Player.Image.Top := ((Self.ClientHeight - Player.Image.Height) div 2) {+ 800};
  143.  
  144.   //Startwerte Spieler festlegen
  145.   Player.Health := 50;
  146.   Player.Damage := 10;
  147.  
  148.   //Enemies spawnen
  149.   SpawnEnemies(AmountWilly);
  150.  
  151.   //Enemies platzieren
  152.   SetEnemyPosition(0, 10, 10);
  153.   SetEnemyPosition(1, 220, 10);
  154.   SetEnemyPosition(2, 430, 10);
  155.   SetEnemyPosition(3, 640, 10);
  156.   SetEnemyPosition(4, 850, 10);
  157.  
  158.  
  159.   //Upgrades zum test am anfang zeigen
  160.   ShowUpgrades;
  161.  
  162.   //Auf collision testen
  163.   {
  164.   for X := 0 to Player.Image.Width - 1 do
  165.       begin
  166.         for Y := 0 to Player.Image.Height - 1 do
  167.         begin
  168.           if (Player.Image.Canvas.Pixels[X, Y] = ArrayEnemies[i].Canvas.Pixels[X + Player.Image.Left, Y + Player.Image.Top]) then
  169.           begin
  170.             PlayerCollision := True;
  171.             Break;
  172.           end;
  173.         end;
  174.         if PlayerCollision then Break;
  175.       end;
  176.       if PlayerCollision then Break;
  177.     end;
  178.    }
  179. end;
  180.  
  181. //Nähesten Gegner ermittlen    (welcher Gegner wird anvisiert)
  182. function GetClosestEnemy(Image: TImage) : integer;
  183. var
  184.   k:                                      integer;
  185.   Distance, MinDistance:                  double;
  186.   DeltaX, DeltaY:                         integer;
  187.   ClosestEnemy:                           integer;
  188. begin
  189.  
  190.   ClosestEnemy := 0;
  191.   MinDistance := MaxInt; //sehr große Zahl
  192.  
  193.   for k := 0 to 4 do    //Später noch zu AmountWilly ändern
  194.   begin
  195.     DeltaX := ArrayEnemies[k].Image.Left + (ArrayEnemies[k].Image.Width div 2) - (Image.Left + (Image.Width div 2));
  196.     DeltaY := ArrayEnemies[k].Image.Top + (ArrayEnemies[k].Image.Height div 2) - (Image.Top + (Image.Height div 2));
  197.     Distance := Sqrt(DeltaX * DeltaX + DeltaY * DeltaY);
  198.     if Distance < MinDistance then
  199.     begin
  200.       MinDistance := Distance;
  201.       ClosestEnemy := k;
  202.     end;
  203.   end;
  204.  
  205.   //Showmessage(Inttostr(ClosestEnemy) + '- Min k - ' + FloatToStr(MinDistance));
  206.   result := ClosestEnemy;
  207.  
  208. end;
  209.  
  210.  
  211. //Pfeil bewegen
  212. procedure TForm1.MoveArrowTimer;
  213. begin
  214.  
  215.    //Pfeil bewegt sich noch unterschiedlich schnell, je nachdem wie weit man vom gegner entfernt steht!!
  216.    //SpeedAdjuster := ClosestDistance;
  217.    PictureOfArrowToShoot.Top := PictureOfArrowToShoot.Top + Round(yDiff/100);
  218.    PictureOfArrowToShoot.Left := PictureOfArrowToShoot.Left + Round(xDiff/100);
  219.  
  220. end;
  221.  
  222. //Pfeil spawnen
  223. procedure SpawnArrow;
  224. var
  225.   Arrow: TArrow;
  226.   ArrowCenterX: integer;
  227.   ArrowCenterY: integer;
  228.   //Closest Enemy
  229.   z : integer;
  230.  
  231. begin
  232.  
  233.   Arrow.Image := TImage.Create(nil);
  234.   Arrow.Image.Parent := Form1;
  235.   Arrow.Image.Width := 50;
  236.   Arrow.Image.Height := 50;
  237.   Arrow.Image.Stretch := true;
  238.  
  239.   //Mitte des Pfeils ermitteln
  240.   ArrowCenterX := Arrow.Image.Width div 2;
  241.   ArrowCenterY := Arrow.Image.Height div 2;
  242.  
  243.   //Pfeil in der Mitte des Spieler platzieren
  244.   Arrow.Image.Left := (Player.Image.Left - ArrowCenterX) + Player.Image.Width div 2;
  245.   Arrow.Image.Top := (Player.Image.Top - ArrowCenterY) + Player.Image.Height div 2;
  246.   LoadImage(Arrow.Image, 'Bilder/BulletTest.jpg');
  247.   PictureOfArrowToShoot := Arrow.Image;
  248.  
  249.   //Abstand zwischen dem nähesten Gegner und Spieler berechnen
  250.   z := GetClosestEnemy(Player.Image);
  251.  
  252.   xDiff := (ArrayEnemies[z].Image.Left + ArrayEnemies[z].Image.Width div 2) - (PictureOfArrowToShoot.Left + ArrayEnemies[z].Image.Width div 2);
  253.   yDiff := (ArrayEnemies[z].Image.Top + ArrayEnemies[z].Image.Height div 2) - (PictureOfArrowToShoot.Top + ArrayEnemies[z].Image.Height div 2);
  254.   //Showmessage('xDiff: ' + IntToStr(xDiff));
  255.   //Showmessage('yDiff: ' + IntToStr(yDiff));
  256.  
  257. end;
  258.  
  259. //Feuerrate
  260. procedure TForm1.ShootArrowTimer(Sender: TObject);
  261. begin
  262.  
  263.   if canShoot = true then
  264.   begin
  265.   ShootArrow.Interval := Player.FireRate;
  266.   SpawnArrow;
  267.   end;
  268.  
  269. end;
  270.  
  271.  
  272. procedure TForm1.Timer1Timer(Sender: TObject);
  273. begin
  274.  
  275.   //Hoch
  276.   if MoveUp = true then
  277.   begin
  278.     Player.Image.Top := Player.Image.Top - 10;
  279.     canShoot := false;
  280.   end
  281.   //Runter
  282.   else If MoveDown = true then
  283.   begin
  284.     Player.Image.Top := Player.Image.Top + 10;
  285.     canShoot := false;
  286.   end
  287.   //Links
  288.   else if MoveLeft = true then
  289.   begin
  290.     Player.Image.Left:= Player.Image.Left - 10;
  291.     canShoot := false;
  292.   end
  293.   //Rechts
  294.   else if MoveRight = true then
  295.   begin
  296.     Player.Image.Left := Player.Image.Left + 10;
  297.     canShoot := false;
  298.   end
  299.   else
  300.   canShoot := true;
  301.  
  302. end;
  303.  
  304.  
  305.  
  306. //Erstes Upgrade geclickt
  307. procedure TForm1.Upgrade1Click(Sender: TObject);
  308. begin
  309.   Showmessage('Upgrade 1 clicked');
  310. end;
  311.  
  312. //Zweites Upgrade geclick
  313. procedure TForm1.Upgrade2Click(Sender: TObject);
  314. begin
  315.   Showmessage('Upgrade 2 clicked');
  316. end;
  317.  
  318. //drittes Upgrade geclickt
  319. procedure TForm1.Upgrade3Click(Sender: TObject);
  320. begin
  321.   Showmessage('Upgrade 3 clicked');
  322. end;
  323.  
  324. //Upgrades
  325. procedure TForm1.ShowUpgrades;
  326. var
  327.   Upgrades : array[1..3] of TImage;
  328.   k : integer;
  329. begin
  330.   //Showmessage('Ugrades werden angezeigt');
  331.   // Create TImage components
  332.   for k := 1 to 3 do
  333.   begin
  334.     Upgrades[k] := TImage.Create(nil);
  335.     Upgrades[k].Parent := Form1;
  336.     Upgrades[k].Height := 100;
  337.     Upgrades[k].Width :=100;
  338.     Upgrades[k].Stretch := true;
  339.     Upgrades[k].Top:= 500;
  340.     Upgrades[k].Left := 50 + (k*200);
  341.   end;
  342.  
  343.  
  344.   // Load images for each TImage component
  345.   Upgrades[1].Picture.LoadFromFile('Bilder/BulletTest.jpg');
  346.   Upgrades[2].Picture.LoadFromFile('Bilder/UserTest.jpg');
  347.   Upgrades[3].Picture.LoadFromFile('Bilder/BulletTest.jpg');
  348.  
  349.   //OnClick Events für jedes Bild
  350.   Upgrades[1].OnClick:= @Upgrade1Click;
  351.   Upgrades[2].OnClick:= @Upgrade2Click;
  352.   Upgrades[3].OnClick:= @Upgrade3Click;
  353.  
  354.  
  355. end;
  356.  
  357. procedure TForm1.FormKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState
  358. );
  359. begin
  360.  
  361.   //Hoch
  362.   if key = VK_UP then
  363.   begin
  364.     MoveUp:= true;
  365.   end;
  366.   //Runter
  367.   if key = VK_DOWN then
  368.   begin
  369.     MoveDown := true;
  370.   end;
  371.   //Links
  372.   if key = VK_LEFT then
  373.   begin
  374.     MoveLeft:= true;
  375.   end;
  376.   //Rechts
  377.   if key = VK_RIGHT then
  378.   begin
  379.     MoveRight := true;
  380.   end;
  381.  
  382.   //Upgrades zeigen
  383.   if key = VK_Space then
  384.   begin
  385.      ShowUpgrades;
  386.   end;
  387.  
  388.  
  389. end;
  390.  
  391. procedure TForm1.FormKeyUp(Sender: TObject; var Key: Word; Shift: TShiftState);
  392. begin
  393.  
  394.   //Hoch
  395.   if key = VK_UP then
  396.   begin
  397.     MoveUp := false;
  398.   end;
  399.   //Runter
  400.   if key = VK_DOWN then
  401.   begin
  402.     MoveDown := false;
  403.   end;
  404.   //Links
  405.   if key = VK_LEFT then
  406.   begin
  407.     MoveLeft:= false;
  408.   end;
  409.   //Rechts
  410.   if key = VK_RIGHT then
  411.   begin
  412.     MoveRight := false;
  413.   end;
  414.  
  415.  
  416. end;
  417.  
  418. end.
  419.  
« Last Edit: January 22, 2023, 11:15:27 pm by LeopardCoder »

furious programming

  • Hero Member
  • *****
  • Posts: 853
Re: Image inside Bitmap
« Reply #5 on: January 22, 2023, 11:22:15 pm »
Do you think it's still worth switching to SDL?

If you're doing this as a school project, then no, it doesn't pay to change anything now. The more so that you think that everything is working properly and efficiently enough. If you are interested in the topic and would like to create any meaningful game (even a small one), you can play with SDL out of curiosity.

Quote
How long would it take to understand SDL well enough to use it?

If you know Free Pascal very well, you can master the absolute basics in a few days, because SDL is simply a set of functions that are simple to use and simple to understand (of course with the help of documentation). More complex things take a few weeks, and to be able to create professional software consisting of complex subsystems and implementing patterns known in the industry, you need long months or even years of study and extensive programming knowledge.

SDL is used for windows and input devices, rendering, and sound handling, but you write all the game logic yourself. To make a simple game, all you need to do is learn to use only a dozen, maybe several dozen functions. On the other hand, you can easily find a lot of tutorials on the web explaining how to use SDL.
« Last Edit: January 22, 2023, 11:23:56 pm by furious programming »
Lazarus 3.2 with FPC 3.2.2, Windows 10 — all 64-bit

Working solo on an acrade, action/adventure game in retro style (pixelart), programming the engine and shell from scratch, using Free Pascal and SDL. Release planned in 2026.

Seenkao

  • Hero Member
  • *****
  • Posts: 546
    • New ZenGL.
Re: Image inside Bitmap
« Reply #6 on: January 22, 2023, 11:37:50 pm »
Привет!
Вы можете попробовать ZenGL. Внутри содержаться примеры, в которых не обязательно многое изучать. Для работы с примитивами, со спрайтами, со звуком, с таймером. Так же показаны примеры столкновений (demo14 - sound). Столкновения реализованы для мыши и кнопкой звука мелодии.

Если это поможет. Успехов!

Google translate:
Hey!
You can try ZenGL. Inside there are examples in which it is not necessary to study much. To work with primitives, with sprites, with sound, with a timer. Collision examples are also shown (demo14 - sound). Collisions are implemented for the mouse and the ringtone sound button.

If it helps. Good luck!
Rus: Стремлюсь к созданию минимальных и достаточно быстрых приложений.

Eng: I strive to create applications that are minimal and reasonably fast.
Working on ZenGL

KodeZwerg

  • Hero Member
  • *****
  • Posts: 2010
  • Fifty shades of code.
    • Delphi & FreePascal
Re: Image inside Bitmap
« Reply #7 on: January 23, 2023, 12:18:03 am »
ZenGL
I think you made a typo there
(blue text)

-translate

мне кажется вы там опечатку сделали
(синий текст)
« Last Edit: Tomorrow at 31:76:97 xm by KodeZwerg »

Seenkao

  • Hero Member
  • *****
  • Posts: 546
    • New ZenGL.
Re: Image inside Bitmap
« Reply #8 on: January 23, 2023, 12:25:10 am »
KodeZwerg, нет, это не опечатка. Это указание на данный архив. Когда люди делают форки проекта, их форки могут отставать. И эта ссылка указывает на первоначальный форк.

К сожаленью, Andru свой сайт удалил. Данный проект это продолжение его дела. Версия 3.12 всё ещё есть, у меня указывается, но только на старый сайт. Которого уже нет... Вот это надо исправить.  :)

Google translate:
KodeZwerg, no, that's not a typo. This is an indication of this archive. When people fork a project, their forks may fall behind. And this link points to the original fork.

Unfortunately, Andru deleted his site. This project is a continuation of his work. Version 3.12 is still there, I have it, but only to the old site. Which no longer exists ... This needs to be fixed. :)
Rus: Стремлюсь к созданию минимальных и достаточно быстрых приложений.

Eng: I strive to create applications that are minimal and reasonably fast.
Working on ZenGL

KodeZwerg

  • Hero Member
  • *****
  • Posts: 2010
  • Fifty shades of code.
    • Delphi & FreePascal
Re: Image inside Bitmap
« Reply #9 on: January 23, 2023, 12:40:43 am »
Just wanted to draw your attention to the fact that your word "guthub" should actually be "github".

-translate

Просто хотел обратить ваше внимание на то, что ваше слово «guthub» на самом деле должно быть «github».
« Last Edit: Tomorrow at 31:76:97 xm by KodeZwerg »

Seenkao

  • Hero Member
  • *****
  • Posts: 546
    • New ZenGL.
Re: Image inside Bitmap
« Reply #10 on: January 23, 2023, 12:51:36 am »
Я как обычно делаю мелкие ошибки...  :-[ Спасибо!

Google translate:
I make small mistakes as usual... :-[ Thank you!
Rus: Стремлюсь к созданию минимальных и достаточно быстрых приложений.

Eng: I strive to create applications that are minimal and reasonably fast.
Working on ZenGL

LeopardCoder

  • New member
  • *
  • Posts: 8
Re: Image inside Bitmap
« Reply #11 on: January 23, 2023, 09:48:03 pm »
Thank you for your answers. However, I am still left with the question:
When I use the code from above, I get the Error: "Identifier not found "IntersectRect". Is there anything I need to add (in the uses for example)?

Code: Pascal  [Select][+][-]
  1. for X := 0 to Player.Image.Width - 1 do
  2.       begin
  3.         for Y := 0 to Player.Image.Height - 1 do
  4.         begin
  5.           if (Player.Image.Canvas.Pixels[X, Y] = ArrayEnemies[i].Canvas.Pixels[X + Player.Image.Left, Y + Player.Image.Top]) then
  6.           begin
  7.             PlayerCollision := True;
  8.             Break;
  9.           end;
  10.         end;
  11.         if PlayerCollision then Break;
  12.       end;
  13.       if PlayerCollision then Break;
  14.     end;

TRon

  • Hero Member
  • *****
  • Posts: 2435
Re: Image inside Bitmap
« Reply #12 on: January 23, 2023, 10:29:11 pm »
When I use the code from above, I get the Error: "Identifier not found "IntersectRect". Is there anything I need to add (in the uses for example)?
perhaps https://www.freepascal.org/docs-html/rtl/types/intersectrect.html ?


furious programming

  • Hero Member
  • *****
  • Posts: 853
Re: Image inside Bitmap
« Reply #13 on: January 23, 2023, 11:28:25 pm »
Is there anything I need to add (in the uses for example)?

Yes — replace the PlayerCollision flag with the goto, to jump out of all loops. 8)

PS: I'm just kidding.
Lazarus 3.2 with FPC 3.2.2, Windows 10 — all 64-bit

Working solo on an acrade, action/adventure game in retro style (pixelart), programming the engine and shell from scratch, using Free Pascal and SDL. Release planned in 2026.

Handoko

  • Hero Member
  • *****
  • Posts: 5131
  • My goal: build my own game engine using Lazarus
Re: Image inside Bitmap
« Reply #14 on: January 24, 2023, 04:51:40 am »
Many useful tips have been given. But I would recommend you to explore:
https://wiki.freepascal.org/Portal:HowTo_Demos

For simple pong game created using LCL components only, try:
Graphics > A very simple single player Pong game
There you can learn:
- How to use TTimer as a game main loop
- Collision detection
- Reading user key input

For more advanced level collision detection, try:
Graphics > Sticky Balls
Graphics > Bullet and asteroid collision detection

For image loading try:
File handling > Load and show image from Resource Stream
File handling > Load a picture and show it
Graphics > How to load a TImagelist at run time

For reading user key input, try:
General > Moving an Object by pressing a key
General > Control an Object's direction using the keyboard
Also try this link:
https://forum.lazarus.freepascal.org/index.php/topic,57229.msg425440.html#msg425440

As you've been told LCL component is not optimized for game programming, not only performance issue, you soon will also get flickering effect. Try this if you want to learn how to minimize flickering effect:
Graphics > Moving circles

Maybe you will interested to build a game map editor someday, try:
Graphics > Game Map Editor

If you want to learn inertia effect, try the demo that comes witth your Lazarus installation:
Lazarus main menu > Tools > Example Projectsspriteexample.lpi

Have fun.

I am controlling the player with a timer that is activated every 10 milliseconds, so the player moves smoothly.

As far as I know the lowest possible value for TTimer.Interval is 15 on Windows 7, any lower value will be ignored.
Read more:
https://learn.microsoft.com/en-us/dotnet/api/system.timers.timer.interval?redirectedfrom=MSDN&view=net-7.0#System_Timers_Timer_Interval

Normally, 60 fps is good for most common games, which means ±16 milliseconds (1000/60). If you really want achieve that small time frame, you can use EpikTimer or delta time technique.
« Last Edit: January 24, 2023, 05:39:56 am by Handoko »

 

TinyPortal © 2005-2018