* * *

Author Topic: Problems with the destructor  (Read 354 times)

krull

  • New member
  • *
  • Posts: 12
Problems with the destructor
« on: May 18, 2017, 07:33:23 pm »
hello!

I still program my vertical shoot 'em up. Now I'm at the point to handle collides. if I make FreeAndNil(self) I catch External Sigsegv. If I use destroy() it brakes in the destructor.
I really don't know which fault i made. Many thanks if you know.

Code: Pascal  [Select]
  1. unit Unit1;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. interface
  6.  
  7. uses
  8.   Windows ,Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, ExtCtrls, BGRABitmap, BGRABitmapTypes;
  9.  
  10. type
  11.   TPlayer = class
  12.     playerPosTimer : TTimer;
  13.     shotTimer: TTimer;
  14.     constructor create();
  15.     procedure playerPosTimerTimer(Sender: TObject);
  16.     procedure playerPaint(p:Point;pOld:Point);
  17.     procedure shotTimerTimer(Sender: TObject);
  18.     procedure shotPaint(shot:TBGRABitmap; p:Point);
  19.     procedure takeDamage(a:Integer);
  20.     destructor destroy();
  21.   private
  22.  
  23.   public
  24.    playerShip : TBGRABitmap;
  25.    shot1, shot2, shot3, shot4: TBGRABitmap;
  26.    pPlayer : Point;
  27.    pFormer: Point;
  28.    shotSpeed : Integer;
  29.    pShot1, pShot2, pShot3, pShot4 : Point;
  30.    leftClick, x : boolean;
  31.    Health: Integer;
  32.   end;
  33.  
  34.   TEnemy1 = class
  35.     enemy1PosTimer : TTimer;
  36.     enemy1ShotTimer : TTimer;
  37.     constructor create(s:integer);
  38.     procedure enemy1PosTimerTimer(Sender: TObject);
  39.     procedure enemy1shotTimerTimer(Sender: TObject);
  40.     procedure enemy1Paint(p:Point;pOld:Point);
  41.     procedure enemy1ShotPaint();
  42.     procedure takeDamage(a:Integer);
  43.     destructor destroy();
  44.   private
  45.  
  46.   public
  47.     enemy1Ship : TBGRABitmap;
  48.     pEnemy1, pEnemy1Former: Point;
  49.     shotSpeed, enemySpeed : Integer;
  50.     strategy: String;
  51.     Health: Integer;
  52.   end;
  53.  
  54.   { TForm1 }
  55.  
  56.   TForm1 = class(TForm)
  57.     procedure FormClick(Sender: TObject);
  58.     procedure FormCreate(Sender: TObject);
  59.     procedure FormKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
  60.  
  61.   private
  62.     { private declarations }
  63.   public
  64.     { public declarations }
  65.   end;
  66.  
  67. var
  68.   Form1: TForm1;
  69.   Player1 : TPlayer;
  70.   Enemy1 : TEnemy1;
  71.   Enemy2 : TEnemy1;
  72.  
  73. implementation
  74.  
  75. {$R *.lfm}
  76.  
  77. { TForm1 }
  78.  
  79. procedure TForm1.FormCreate(Sender: TObject);
  80. begin
  81.  Player1:=TPlayer.create;
  82.  Enemy1:=TEnemy1.create(1);
  83.  Enemy2:=TEnemy1.create(2);
  84. end;
  85.  
  86. procedure TForm1.FormKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
  87. begin
  88.   if (Key=VK_ESCAPE) then halt(0)
  89. end;
  90.  
  91.  
  92. procedure TForm1.FormClick(Sender: TObject);
  93. begin
  94.   Player1.leftClick:=true;
  95. end;
  96.  
  97. constructor TPlayer.Create();
  98. begin
  99.   playerShip := TBGRABitmap.Create('ship0.png');
  100.   pPlayer:= Mouse.CursorPos;
  101.   pPlayer:= Form1.ScreenToClient(pPlayer);
  102.   shotSpeed:=10;
  103.   health:=2;
  104.   leftClick:= false;
  105.   pShot1.y:=1; pShot2.y:=1; pShot3.y:=1; pShot4.y:=1;
  106.   playerPosTimer := TTimer.Create(nil);
  107.   shotTimer := TTimer.create(nil);
  108.   playerPosTimer.Interval := 25;
  109.   shotTimer.Interval := 25;
  110.   playerPosTimer.OnTimer := @playerPosTimerTimer;
  111.   shotTimer.onTimer := @shotTimerTimer;
  112.   playerPosTimer.Enabled := true;
  113.   shotTimer.Enabled := true;
  114. end;
  115.  
  116. procedure TPlayer.playerPosTimerTimer(Sender: TObject);
  117. begin
  118.   pFormer:= pPlayer;
  119.   pPlayer:= Mouse.CursorPos;
  120.   pPlayer:= Form1.ScreenToClient(pPlayer);
  121.   if pPlayer.x < pFormer.x then
  122.   begin
  123.     playerShip.free;
  124.     playerShip := TBGRABitmap.Create('ship-4.png');
  125.   end
  126.     else if pPlayer.x > pFormer.x then
  127.     begin
  128.     playerShip.free;
  129.     playerShip := TBGRABitmap.Create('ship4.png');
  130.     end
  131.     else
  132.     begin
  133.       playerShip.free;
  134.       playerShip := TBGRABitmap.Create('ship0.png');
  135.     end;
  136.   playerPaint(PPlayer,pFormer);
  137. end;
  138.  
  139.  
  140.  
  141. procedure TPlayer.playerPaint(p:Point;pOld:Point);
  142. begin
  143.   Form1.Canvas.FillRect(pOld.x-round(0.5*playerShip.Width),pOld.y-round(0.5*playerShip.Height),pOld.x+playerShip.Width-round(0.5*playerShip.Width),pOld.y+playerShip.Height-round(0.5*playerShip.Height));
  144.   playerShip.Draw(Form1.Canvas, p.x-round(0.5*playerShip.Width),p.y-round(0.5*playerShip.Height),false);
  145. end;
  146.  
  147. procedure TPlayer.shotTimerTimer(Sender: TObject);
  148. begin
  149.   if (leftClick = true) and not assigned(shot1) then
  150.   begin
  151.     shot1:= TBGRABitmap.Create('Beam1.png');
  152.     pShot1.x:=pPlayer.x- round(0.5*shot1.width);
  153.     pShot1.y:=pPlayer.y- round(0.5*playerShip.Height)-shot1.Height;
  154.     shotPaint(shot1,pShot1);
  155.     leftClick:=false;
  156.   end;
  157.   if (leftClick = true) and not assigned(shot2) and assigned(shot1) then
  158.   begin
  159.     shot2:= TBGRABitmap.Create('Beam1.png');
  160.     pShot2.x:=pPlayer.x- round(0.5*shot2.width);
  161.     pShot2.y:=pPlayer.y- round(0.5*playerShip.Height)-shot2.Height;
  162.     shotPaint(shot2,pShot2);
  163.     leftClick:=false;
  164.   end;
  165.   if (leftClick = true) and not assigned(shot3) and assigned(shot2) and assigned(shot1)then
  166.   begin
  167.     shot3:= TBGRABitmap.Create('Beam1.png');
  168.     pShot3.x:=pPlayer.x- round(0.5*shot3.width);
  169.     pShot3.y:=pPlayer.y- round(0.5*playerShip.Height)-shot3.Height;
  170.     shotPaint(shot3,pShot3);
  171.     leftClick:=false;
  172.   end;
  173.   if (leftClick = true) and assigned(shot1) and assigned(shot2) and assigned(shot3) and not assigned(shot4) then
  174.   begin
  175.     shot4:= TBGRABitmap.Create('Beam1.png');
  176.     pShot4.x:=pPlayer.x- round(0.5*shot4.width);
  177.     pShot4.y:=pPlayer.y- round(0.5*playerShip.Height)-shot4.Height;
  178.     shotPaint(shot4,pShot4);
  179.     leftClick:=false;
  180.   end;
  181.  
  182.   if assigned(shot1) or assigned(shot2) or assigned(shot3) or assigned(shot4) then
  183.   begin
  184.     if pShot1.y<=0 then
  185.     begin
  186.       Form1.canvas.FillRect(pShot1.x, pShot1.y, pShot1.x+shot1.Width, pShot1.y+shot1.Height);
  187.       FreeAndNil(shot1);
  188.       pShot1.y:=1;
  189.     end
  190.     else if assigned(shot1) then
  191.     begin
  192.       pShot1.y:= pShot1.y-shotSpeed;
  193.       shotPaint(shot1,pShot1);
  194.     end;
  195.     if (pShot2.y <= 0) then
  196.     begin
  197.       Form1.canvas.FillRect(pShot2.x, pShot2.y, pShot2.x+shot2.Width, pShot2.y+shot2.Height);
  198.       FreeAndNil(shot2);
  199.       pShot2.y:=1;
  200.     end
  201.     else if assigned(shot2) then
  202.     begin
  203.       pShot2.y:= pShot2.y-shotSpeed;
  204.       shotPaint(shot2,pShot2);
  205.     end;
  206.     if (pShot3.y <= 0) then
  207.     begin
  208.       Form1.canvas.FillRect(pShot3.x, pShot3.y, pShot3.x+shot3.Width, pShot3.y+shot3.Height);
  209.       FreeANdNil(shot3);
  210.       pShot3.y:=1;
  211.     end
  212.     else if assigned(shot3) then
  213.     begin
  214.       pShot3.y:= pShot3.y-shotSpeed;
  215.       shotPaint(shot3,pShot3);
  216.     end;
  217.     if (pShot4.y <= 0) then
  218.     begin
  219.       Form1.canvas.FillRect(pShot4.x, pShot4.y, pShot4.x+shot4.Width, pShot4.y+shot4.Height);
  220.       FreeANdNil(shot4);
  221.       pShot4.y:=1;
  222.     end
  223.     else if assigned(shot4) then
  224.     begin
  225.       pShot4.y:= pShot4.y-shotSpeed;
  226.       shotPaint(shot4,pShot4);
  227.     end;
  228.   end;
  229.  
  230. end;
  231.  
  232. procedure TPlayer.shotPaint(shot:TBGRABitmap; p:Point);
  233. begin
  234.  Form1.canvas.FillRect(p.x, p.y+shotSpeed, p.x+shot.Width, p.y+shot.Height+shotSpeed);
  235.  shot.draw(Form1.Canvas,p.x,p.y,true);
  236. end;
  237.  
  238. procedure TPlayer.takeDamage(a:Integer);
  239. begin
  240.  
  241. end;
  242.  
  243. destructor TPLayer.destroy();
  244. begin
  245.  
  246. end;
  247.  
  248. constructor TEnemy1.create(s:integer);
  249. begin
  250.  if s=1 then strategy:='Attack' else strategy:='Defense';
  251.   enemy1Ship:=TBGRABitmap.Create('Enemy1.png');
  252.   shotSpeed:=15;
  253.   enemySpeed:=4;
  254.   health:=1;
  255.   enemy1PosTimer:=TTimer.create(nil);
  256.   enemy1ShotTimer:=TTimer.create(nil);
  257.   enemy1PosTimer.Interval:= 25;
  258.   enemy1ShotTimer.Interval:= 25;
  259.   enemy1PosTimer.onTimer:= @enemy1PosTimerTimer;
  260.   enemy1ShotTimer.onTimer:= @enemy1shotTimerTimer;
  261.   enemy1PosTimer.Enabled:=true;
  262.   enemy1ShotTimer.Enabled:=true;
  263.   PEnemy1.y:=0; pEnemy1.x:=round(0.5*Form1.Width);
  264.   enemy1Ship.Draw(Form1.Canvas,pEnemy1.x,pEnemy1.y,false);
  265. end;
  266. procedure TEnemy1.enemy1PosTimerTimer(Sender: TObject);
  267. label 1;
  268. var a,g,h : Double;
  269. begin
  270.   if strategy='Attack' then
  271.   begin
  272.     if (Player1.pPlayer.x-pEnemy1.x>0) or (Player1.pPlayer.x-pEnemy1.x<0)then
  273.     begin
  274.       a:=arctan((Player1.pPlayer.y-pEnemy1.y+200)/(Player1.pPlayer.x-pEnemy1.x));
  275.       g:=sin(a)*enemySpeed;
  276.       if g=0 then Goto 1;
  277.       h:=sqrt(sqr(enemySpeed))-(sqr(g));
  278.       if (pEnemy1.x-Player1.pPlayer.x < 0) and (pEnemy1.y-Player1.pPlayer.y+200 < 0) then
  279.       begin
  280.         pEnemy1Former:=pEnemy1; pEnemy1.x:=pEnemy1.x+abs(round(h)); pEnemy1.y:=pEnemy1.y+abs(round(g));
  281.         enemy1Paint(pEnemy1,pEnemy1Former);
  282.         Goto 1;
  283.       end
  284.       else if (pEnemy1.x-Player1.pPlayer.x < 0) and (pEnemy1.y-Player1.pPlayer.y+200 > 0) then
  285.       begin
  286.         pEnemy1Former:=pEnemy1; pEnemy1.x:=pEnemy1.x+abs(round(h)); pEnemy1.y:=pEnemy1.y-abs(round(g));
  287.         enemy1Paint(pEnemy1,pEnemy1Former);
  288.         Goto 1;
  289.       end
  290.       else if (pEnemy1.x-Player1.pPlayer.x > 0) and (pEnemy1.y-Player1.pPlayer.y+200< 0) then
  291.       begin
  292.         pEnemy1Former:=pEnemy1; pEnemy1.x:=pEnemy1.x-abs(round(h)); pEnemy1.y:=pEnemy1.y+abs(round(g));
  293.         enemy1Paint(pEnemy1,pEnemy1Former);
  294.         Goto 1;
  295.       end
  296.       else if (pEnemy1.x-Player1.pPlayer.x > 0) and (pEnemy1.y-Player1.pPlayer.y+200 >0) then
  297.       begin
  298.         pEnemy1Former:=pEnemy1; pEnemy1.x:=pEnemy1.x-abs(round(h)); pEnemy1.y:=pEnemy1.y-abs(round(g));
  299.         enemy1Paint(pEnemy1,pEnemy1Former);
  300.         Goto 1;
  301.       end;
  302.     end;
  303.   end;
  304.  
  305.   if strategy='Defense' then
  306.   begin
  307.     if pEnemy1.y<100 then
  308.     begin
  309.       pEnemy1Former.x:=pEnemy1.x;
  310.       pEnemy1Former.y:=pEnemy1.y;
  311.       pEnemy1.y:=pEnemy1.y+EnemySpeed;
  312.       enemy1Paint(pEnemy1,pEnemy1Former);
  313.       Goto 1;
  314.     end;
  315.     if pEnemy1.x<=0 then
  316.     begin
  317.       pEnemy1Former.x:=pEnemy1.x;
  318.       pEnemy1Former.y:=pEnemy1.y;
  319.       pEnemy1.x:=pEnemy1.x+EnemySpeed;
  320.       enemy1Paint(pEnemy1,pEnemy1Former);
  321.       Goto 1;
  322.     end;
  323.     if pEnemy1.x>=Form1.width then
  324.     begin
  325.       pEnemy1Former.x:=pEnemy1.x;
  326.       pEnemy1Former.y:=pEnemy1.y;
  327.       pEnemy1.x:=pEnemy1.x-EnemySpeed;
  328.       enemy1Paint(pEnemy1,pEnemy1Former);
  329.       Goto 1;
  330.     end;
  331.     if pEnemy1.x=round(0.5*Form1.Width) then
  332.     begin
  333.       Randomize;
  334.       if random(1)>0 then
  335.       begin
  336.         pEnemy1Former.x:=pEnemy1.x;
  337.         pEnemy1Former.y:=pEnemy1.y;
  338.         pEnemy1.x:=pEnemy1.x+EnemySpeed;
  339.         enemy1Paint(pEnemy1,pEnemy1Former);
  340.         Goto 1;
  341.       end
  342.       else
  343.       begin
  344.         pEnemy1Former.x:=pEnemy1.x;
  345.         pEnemy1Former.y:=pEnemy1.y;
  346.         pEnemy1.x:=pEnemy1.x-EnemySpeed;
  347.         enemy1Paint(pEnemy1,pEnemy1Former);
  348.         Goto 1;
  349.       end;
  350.     end;
  351.     if pEnemy1.x>pEnemy1Former.x then
  352.     begin
  353.       pEnemy1Former.x:=pEnemy1.x;
  354.       pEnemy1Former.y:=pEnemy1.y;
  355.       pEnemy1.x:=pEnemy1.x+EnemySpeed;
  356.       enemy1Paint(pEnemy1,pEnemy1Former);
  357.       Goto 1;
  358.     end
  359.     else if pEnemy1.x<pEnemy1Former.x then
  360.     begin
  361.       pEnemy1Former.x:=pEnemy1.x;
  362.       pEnemy1Former.y:=pEnemy1.y;
  363.       pEnemy1.x:=pEnemy1.x-EnemySpeed;
  364.       enemy1Paint(pEnemy1,pEnemy1Former);
  365.       Goto 1;
  366.     end;
  367.   end;
  368.   1:
  369.   if (pEnemy1.x<Player1.pPlayer.x) and (Player1.pPlayer.x<pEnemy1.x+Enemy1Ship.Width) and (pEnemy1.y<Player1.pPlayer.y) and (Player1.pPlayer.y<pEnemy1.y+Enemy1Ship.height) then
  370.   begin
  371.     takeDamage(1);
  372.     Player1.takeDamage(1);
  373.   end;
  374. end;
  375.  
  376. procedure TEnemy1.enemy1shotTimerTimer(Sender: TObject);
  377. begin
  378.  
  379. end;
  380.  
  381. procedure TEnemy1.enemy1Paint(p:point;pOld:point);
  382. begin
  383.   Form1.Canvas.FillRect(pOld.x,pOld.y,pOld.x+Enemy1Ship.Width,pOld.y+Enemy1Ship.Height);
  384.   enemy1Ship.Draw(Form1.Canvas, p.x,p.y,false);
  385. end;
  386.  
  387. procedure TEnemy1.enemy1ShotPaint();
  388. begin
  389.  
  390. end;
  391.  
  392. procedure TEnemy1.takeDamage(a:Integer);
  393. begin
  394.   health:=health-a;
  395.   if health<=0 then FreeAndNil(self);
  396. end;
  397.  
  398. destructor TEnemy1.destroy();
  399. begin
  400.   Form1.Canvas.FillRect(pEnemy1.x,pEnemy1.y,pEnemy1.x+Enemy1Ship.Width,pEnemy1.y+Enemy1Ship.Height);
  401. end;
  402.  
  403. end.
  404.  
  405.  

Eugene Loza

  • Sr. Member
  • ****
  • Posts: 449
    • My "almost daily" development blog
Re: Problems with the destructor
« Reply #1 on: May 18, 2017, 08:10:23 pm »
Add "inherited" in destroy. Otherwise it'll work weird (won't work).
FreeAndNil(self) ------- that's obviously you shouldn't do! The classes shouldn't commit suicide (at least in virtual mehtods).
As a quick workaround you might want to make a variable "FreeMe: boolean" and free the class by external routine.

In your specific case (I can see only two enemies hardcoded) I'd recommend against freeing them at all and freeing them only in Form destructor.
« Last Edit: May 18, 2017, 08:15:12 pm by Eugene Loza »
My Free and Open Source games in Lazarus/FreePascal/CastleGameEngine:
https://decoherence.itch.io/
(and some ancient games in Turbo Pascal too)
Sources are here: https://github.com/eugeneloza?tab=repositories

 

Recent

Get Lazarus at SourceForge.net. Fast, secure and Free Open Source software downloads Open Hub project report for Lazarus