Canvas.Draw verursacht SIGSEGV-Fehler

Für Probleme bezüglich Grafik, Audio, GL, ACS, ...
Antworten
ccaa
Beiträge: 2
Registriert: Mo 24. Aug 2020, 14:58

Canvas.Draw verursacht SIGSEGV-Fehler

Beitrag von ccaa »

Mahlzeit zusammen,

ich habe nach einiger Zeit mal wieder angefangen ein wenig an meinen Programmierkenntnissen zu arbeiten, und um zu schauen was so hängen geblieben ist wollte ich ein sehr simples Memory-Spiel bauen. Leider hänge ich nun schon so ziemlich am Anfang ziemlich fest, da das Programm beim Zeichnen auf eine PaintBox einen Fehler auswirft
Projekt myMemory hat Exception-Klasse >>External: SIGSEGV<< ausgelöst. In Datei '.\include\canvas.inc' in Zeile 25
.

Mein Plan war, eine PaintBox zu nutzen und alle Grafiken direkt auf die PaintBox zu zeichnen. Beim initialisieren zeichne ich ein Hintergrundbild auf die Box und lasse dann (in Reihen/Spalten angeordnet) die Memory-Karten zeichnen, zu Beginn mit der Rückseite nach oben. Beim Klick auf die PaintBox ermittle ich die Klickposition, anhand der ich dann die angeklickte Karte identifiziere. Ist die Karte noch nicht sichtbar (die Info habe ich in Records in einem Array gespeichert), soll sie umgedreht werden.

Hier die aus meiner Sicht relevanten Code-Abschnitte:

Code: Alles auswählen

 type
  TCard = Record
     value: Integer;
     visible: Boolean;
  end;
  
  type TCardArray = Array of TCard; 

Code: Alles auswählen

procedure TForm1.BitBoxMouseUp(Sender: TObject; Button: TMouseButton;
	Shift: TShiftState; X, Y: Integer);
var
  r, c: Integer;
begin
  r := y div CBgBit.Height;
  c := x div CBgBit.Width;
  GameField[c][r].visible := not GameField[c][r].visible;
  BitBox.Repaint;
end; 

Code: Alles auswählen

procedure TForm1.BitBoxPaint(Sender: TObject);
var
  r, c: Integer;
begin
  BitBox.Canvas.Draw(0, 0, bgBit);
  for r := 0 to CBoardRows - 1 do
  begin
    for c := 0 to CBoardCols - 1 do
    begin
      if GameField[c][r].visible then
      	BitBox.Canvas.Draw(c * CBgBit.Width, r * CBgBit.Height, Imgs[GameField[c][r].value]) else
	BitBox.Canvas.Draw(c * CBgBit.Width, r * CBgBit.Height, CBgBit);
      end;
  end;
end;  
Anfangs dachte ich, dass alles fehlerfrei läuft, da erst alles wie erwartet geklappt hat. Habe ich auf eine Karte mit "Rücken nach oben" geklickt wurde sie "umgedreht", und andersrum. Erst nach mehreren Klicks trat dann der Fehler auf. Habe schon versucht herauszufinden, ob der Fehler immer an einer bestimmten Position (Reihe / Spalte) auftritt oder immer beim selben Bild, aber auch da konnte ich keine Logik erkennen, nach der der Fehler auftritt. Bei jedem Neustart des Programms ist der Fehler an einer anderen Stelle aufgetreten...

Ich hoffe, dass mir jemand helfen kann :wink:

Der Vollständigkeit halber hier noch der komplette Code...

Code: Alles auswählen

unit Unit1;

{$mode objfpc}{$H+}

interface

uses
	Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, ExtCtrls, Math;

type
 TIntegerArray = Array of Integer;

 type
  TCard = Record
     value: Integer;
     visible: Boolean;
	end;

  type TCardArray = Array of TCard;

	{ TForm1 }

 TForm1 = class(TForm)
		BitBox: TPaintBox;
		procedure BitBoxMouseUp(Sender: TObject; Button: TMouseButton;
			Shift: TShiftState; X, Y: Integer);
  procedure BitBoxPaint(Sender: TObject);
  procedure FormCreate(Sender: TObject);
	procedure FormDestroy(Sender: TObject);
	private
     GameField: Array of TCardArray;
     Imgs: Array of TBitmap;
     BgBit: TBitmap;
     CBgBit: TBitmap;
     procedure CreateGameField;
     procedure LoadBitmaps;
     procedure ShuffleArr(arr: TIntegerArray; loops: Integer = 5);
	public

	end;

var
	Form1: TForm1;

const
  CImgCount = 12;
  CBoardCols = 6;
  CBoardRows = 4;

implementation

{$R *.lfm}

{ TForm1 }

procedure TForm1.FormCreate(Sender: TObject);
begin
  Randomize;
  CreateGameField;
  LoadBitmaps;
  Form1.Width := BgBit.Width;
  Form1.Height := BgBit.Height;
end;

procedure TForm1.FormDestroy(Sender: TObject);
var
  i: Integer;
begin
	BgBit.Destroy;
  CBgBit.Destroy;
  for i := 0 to Length(Imgs) - 1 do
  	Imgs[i].Destroy;
end;

procedure TForm1.ShuffleArr(arr: TIntegerArray; loops: Integer = 5);
var
  tmp: TIntegerArray;
  i, r: Integer;
begin
  while loops > 0 do
  begin
	  tmp := Copy(arr, 0, Length(arr));
	  for i := 0 to Length(arr) - 1 do
	  begin
			r := Random(Length(arr));
	    while tmp[r] = -1 do
				r := Random(Length(arr));
			arr[i] := tmp[r];
	    tmp[r] := -1;
		end;
    dec(loops);
	end;
end;

procedure TForm1.CreateGameField;
var
  i, j: Integer;
  arr: TIntegerArray;
begin
  SetLength(arr, CBoardRows * CBoardCols);
  for i := 0 to Length(arr) - 1 do
  	if(i < (Length(arr) - 1) div 2) then
  		arr[i] := i else
      arr[i] := i - (Length(arr) - 1) div 2;
  ShuffleArr(arr);
  SetLength(GameField, CBoardCols);
	for i := 0 to CBoardCols - 1 do
  begin
		SetLength(GameField[i], CBoardRows);
    for j := 0 to CBoardRows - 1 do
    begin
      GameField[i][j].value := arr[i * CBoardRows + j];
      GameField[i][j].visible := False;
		end;
	end;
end;

procedure TForm1.LoadBitmaps;
var
  i: Integer;
  path, s: string;
begin
	path := ExtractFilePath(ParamStr(0));
  BgBit := TBitmap.Create;
  BgBit.LoadFromFile(path + 'bg.bmp');
  CBgBit := TBitmap.Create;
  CBgBit.LoadFromFile(path + 'bg_c.bmp');
  SetLength(Imgs, CImgCount);
	for i := 0 to CImgCount - 1 do
  begin
    s := path + IntToStr(i) + '.bmp';
    Imgs[i] := TBitmap.Create;
		Imgs[i].LoadFromFile(s);
	end;
end;


procedure TForm1.BitBoxPaint(Sender: TObject);
var
  r, c: Integer;
begin
	BitBox.Canvas.Draw(0, 0, bgBit);
  for r := 0 to CBoardRows - 1 do
  begin
    for c := 0 to CBoardCols - 1 do
    begin
      if GameField[c][r].visible then
      	BitBox.Canvas.Draw(c * CBgBit.Width, r * CBgBit.Height, Imgs[GameField[c][r].value]) else
				BitBox.Canvas.Draw(c * CBgBit.Width, r * CBgBit.Height, CBgBit);
		end;
	end;
end;

procedure TForm1.BitBoxMouseUp(Sender: TObject; Button: TMouseButton;
	Shift: TShiftState; X, Y: Integer);
var
  r, c: Integer;
begin
	r := y div CBgBit.Height;
  c := x div CBgBit.Width;
  showmessage(inttostr(gamefield[c][r].value) + ' | ' +
  	inttostr(c * cbgbit.Width) + ' | ' + inttostr(r * cbgbit.Height));
  GameField[c][r].visible := not GameField[c][r].visible;
  BitBox.Repaint;
end;

end.

Benutzeravatar
Winni
Beiträge: 1577
Registriert: Mo 2. Mär 2009, 16:45
OS, Lazarus, FPC: Laz2.2.2, fpc 3.2.2
CPU-Target: 64Bit
Wohnort: Fast Dänemark

Re: Canvas.Draw verursacht SIGSEGV-Fehler

Beitrag von Winni »

Hi!

Ich sehe da im Moment nix, aber was ich machen würde, ist bei MouseUp mir mal die Werte von r und c anzeigen lassen - vielleicht werden da die Array-Grenzen gesprengt, was gerne in einem SIGSEV endet.

Und wenn Du vorher weisst, dass die Karten 6x4 ausgelegt werden, würde ich ja kein dynamisches Array nehmen - zu fehleranfällig. Ein
array[0..5,0..3] of TCard

tut es doch auch.

Winni

Sieben
Beiträge: 202
Registriert: Mo 24. Aug 2020, 14:16
OS, Lazarus, FPC: Ubuntu Xenial 32, Lazarus 2.2.0, FPC 3.2.2
CPU-Target: i386

Re: Canvas.Draw verursacht SIGSEGV-Fehler

Beitrag von Sieben »

Hat, so wie du es beschreibst, vermutlich nicht direkt mit dem Fehler zu tun, aber mir fällt auf, dass du für deine Bitmaps abschließend Destroy aufrufst statt Free. Das solltest du auf jeden Fall ändern.

ccaa
Beiträge: 2
Registriert: Mo 24. Aug 2020, 14:58

Re: Canvas.Draw verursacht SIGSEGV-Fehler

Beitrag von ccaa »

Die Werte für r und c hab ich geprüft, da ist (leider...) alles so wie es sein sollte.

Eure Tipps zum Thema statische/dynamische Arrays und destroy/free nehme ich gerne mit, danke dafür :)

Benutzeravatar
Winni
Beiträge: 1577
Registriert: Mo 2. Mär 2009, 16:45
OS, Lazarus, FPC: Laz2.2.2, fpc 3.2.2
CPU-Target: 64Bit
Wohnort: Fast Dänemark

Re: Canvas.Draw verursacht SIGSEGV-Fehler

Beitrag von Winni »

Hi!

Und das Mischen machst Du für meinen Geschmack zu kompliziert.
Hier mein Vorschlag:

Code: Alles auswählen

procedure TForm1.ShuffleArr(arr: TIntegerArray; loops: Integer = 5);
var
  i,a,b, max, tausch: Integer;
begin
max := loops * length(arr);
for i := 0 to max do
   begin
   a := random (length(arr));
   b := random (length(arr));
  tausch := arr[a];
  arr[a] := arr[b];
  arr[b] := tausch;
   end; // i

end;
Einfach immer zwei zufällige Karten austauschen.
Worst case: Die Karte wird mit sich selbst getauscht ....

Winni

shokwave
Beiträge: 470
Registriert: Do 15. Nov 2007, 16:58
OS, Lazarus, FPC: Win11/Ubuntu Budgie (L 3.0 FPC 3.2.2)
CPU-Target: i386, x64
Wohnort: Gera

Re: Canvas.Draw verursacht SIGSEGV-Fehler

Beitrag von shokwave »

Hi,

für mich sieht diese Stelle komisch aus:

Code: Alles auswählen

for i := 0 to Length(arr) - 1 do //von 0 bis 23
    if (i < (Length(arr) - 1) div 2) then //wenn i kleiner 11 ist, also bis 10
      arr[i] := i
    else
      arr[i] := i - (Length(arr) - 1) div 2; //am Ende der Schleife arr[23] := 23-(24-1) div 2 = 23-11=12
Ich denke du kommst hier am Ende der Schleife über die Arraygrenze von Imgs[] die ja nur von 0 bis 11 geht.

Ungetestet, aber ich glaube so könnte es gehen:

Code: Alles auswählen

  for i := 0 to Length(arr) - 1 do
    if (i <= (Length(arr) - 1) div 2) then // kleiner/gleich 11, also bis 11
      arr[i] := i
    else //ab 12
      arr[i] := i - Length(arr) div 2; //arr[23]:=23-12=11
mfg Ingo

Benutzeravatar
af0815
Lazarusforum e. V.
Beiträge: 6198
Registriert: So 7. Jan 2007, 10:20
OS, Lazarus, FPC: FPC fixes Lazarus fixes per fpcupdeluxe (win,linux,raspi)
CPU-Target: 32Bit (64Bit)
Wohnort: Burgenland
Kontaktdaten:

Re: Canvas.Draw verursacht SIGSEGV-Fehler

Beitrag von af0815 »

Generelle Frage, hast du das Debugging in den Projekteinstellungen aufgedreht ? Am besten mal ein Debugging und ein Release dort erstellen.
Blöd kann man ruhig sein, nur zu Helfen muss man sich wissen (oder nachsehen in LazInfos/LazSnippets).

Antworten