Jump to content
Willicious

Beginner to Delphi. Have no idea what I'm doing wrong here

Recommended Posts

Hi all, complete beginner to Delphi/Embarcadero here.

 

I'm following this tutorial in an attempt to make a very basic movement-based game.

 

I'm getting a bunch of errors, so I've dialled it back and I'm doing everything a step at a time and checking for errors. Can anyone see what's going wrong here? I've tried so many things to solve this error, but since the error message is extremely vague ("E2008 Incompatible types"), it makes troubleshooting very difficult.

I've posted the entire code so far. The error is with the word "then" on the line if Key = 'w' then Movement := 'down';

unit Unit1;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.ExtCtrls, Vcl.StdCtrls;

type
  TForm1 = class(TForm)
    Shape1: TShape;
    KeyboardInput: TListBox;
    procedure FormCreate(Sender: TObject);
    procedure KeyboardInputKeyDown(Sender: TObject; var Key: Word;
      Shift: TShiftState);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;
  Movement: String;

implementation

{$R *.dfm}

procedure TForm1.FormCreate(Sender: TObject);
begin
  KeyboardInput.SetFocus();
end;

procedure TForm1.KeyboardInputKeyDown(Sender: TObject; var Key: Word;
  Shift: TShiftState);
begin
  if Key = 'w' then Movement := 'down';
end;

end.

 

Incidentally, if anyone knows of any good online tutors for Delphi/Embarcadero (i.e. live tutoring, NOT video tutorials), I'm interested!

 

Thanks,

 

Will

Share this post


Link to post

Try the follow

 

if Key = Ord('w') then Movement := 'down';

 

  • Thanks 1

Share this post


Link to post

1) pay attention on "param type", in case: "KEY = WORD type", or be, "a number" not a "letter"

Byte, Word, SmallInt, Integer, Double, etc..  = numberic value

string, char, shortstring, etc.. = character value (number, letter, symbol quoted)

 

2) if you need that a "control" stay on focus in your form, try use property "ActiveControl" on form (in design-time or runtime) too! no need a coding, at all.

Share this post


Link to post

Interesting, I looked at the video Will is following take a look at 2:54. The tutorial's ListBox OnKeyDown event has an additional parameter var KeyChar : char that the instructor is using therefore the 'w' instead of Ord('w').

 

My ListBox doesn't have that parameter and neither does Will's. Anybody know what's going on? 2:06 shows Delphi 10.2

Share this post


Link to post

Here you go Will.

 

The tutorial is a multi platform application, you started a VCL application, the controls have different parameters.

  • Thanks 1

Share this post


Link to post

Thanks for the replies, everyone! gkobler's reply solved the error, but the keydown doesn't translate to movement.

 

I've now removed the listbox and I'm applying KeyDown code directly to the form. The form now just has a square. I'm trying to make the square move downwards by 10px when 'd' is pressed:

 

type
  TForm1 = class(TForm)
    Square: TShape;
    ActualMovement: TTimer;
    procedure ActualMovementTimer(Sender: TObject);
    procedure FormKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;
  Movement: String;

implementation

{$R *.dfm}

procedure TForm1.ActualMovementTimer(Sender: TObject);
begin
  if Movement = 'Down' then Square.Top := + 10;
end;

procedure TForm1.FormKeyDown(Sender: TObject; var Key: Word;
  Shift: TShiftState);
begin
  if Key = Ord('w') then Movement := 'Down';
end;

end.

I figured that Square.Top would do this, but perhaps there is something incorrect about the line if Movement = 'Down' then Square.Top := +10;

Share this post


Link to post
1 minute ago, Willicious said:

if Movement = 'Down' then Square.Top := +10;

If Movement = 'Down' then

  Square.Top:= Square.Top + 10;

Share this post


Link to post

Got rid of the TTimer altogether, to even further simplify what I'm trying to do. The square is not moving:

 

type
  TForm1 = class(TForm)
    Square: TShape;
    procedure FormKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;
  Movement: String;

implementation

{$R *.dfm}

procedure TForm1.FormKeyDown(Sender: TObject; var Key: Word;
  Shift: TShiftState);
begin
  if Key = Ord('w') then Square.Top := Square.Top + 10;
end;

end.

 

Share this post


Link to post

If you are following the tutorial you should start a new project. Select New / Multi-Device Application not Windows VCL Application. In this case the Windows VCL TListBox component does not have the " var KeyChar : char" parameter that the Multi-Device TListBox component has. Many others could be different as well and that would create serious cause of confusion.

  • Thanks 1

Share this post


Link to post

Follow the tutorial. Setting "Movement" in the event is just the first step, the actual moving of the image is done later with a timer.

Share this post


Link to post

For me this is working with VCL. I set the timer to 250ms.

 

unit Unit1;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.ExtCtrls;

type
  TDirection = (dinone, diup, didown, dileft, diright);

  TForm1 = class(TForm)
    box: TShape;
    Timer1: TTimer;
    procedure Timer1Timer(Sender: TObject);
    procedure FormKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
  private
    { Private declarations }
    fDirection: TDirection;
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}



procedure TForm1.FormKeyDown(Sender: TObject; var Key: Word;
  Shift: TShiftState);
begin
  case key of
    ord('W'): fdirection:=diup;
    ord('A'): fDirection:=diLeft;
    ord('D'): fDirection:=diRight;
    ord('X'): fDirection:=diDown;
  else
    fDirection:=diNone;
  end;
end;

procedure TForm1.Timer1Timer(Sender: TObject);
begin
  case fDirection of
    diup : box.Top:=box.top-10;
    didown : box.Top:=box.Top+10;
    dileft: box.left:=box.Left-10;
    diright: box.Left:=box.Left+10;
  end;
end;

end.

 

  • Like 1

Share this post


Link to post

@GaryThanks for the suggestion, but tbh I'd like to try doing it as a Windows app. Later, I'll try again following the tutorial as you've suggested, for sure.

 

For now though, the important thing is that I learn to bind keyboard input to movement.

Share this post


Link to post

 

@Lajos JuhászHow would you specify a single line of keyboard input?

 

if key = Ord('w') then ... isn't working for me

 

OK, it works if you make the letter capital, so:

 

if key = Ord('W') then

Edited by Willicious

Share this post


Link to post

Thanks so much for your help so far, everyone! Apologies that these are such basic questions! 🤦‍♂️

Share this post


Link to post

if key is a char:

if Key in ['W','w'] then....

if Lower(key) = 'w' then...

 

if key is a Word:

if char(key) in ['W', 'w'] then...

Edited by programmerdelphi2k
  • Like 1

Share this post


Link to post

Hello VCL world... :classic_cheerleader:

type
  TForm1 = class(TForm)
    Image1: TImage;
    ImageBackground: TImage;
    procedure FormCreate(Sender: TObject);
    procedure FormKeyPress(Sender: TObject; var Key: Char);
    procedure FormDestroy(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

var
  LStep      : integer = 5;
  LHelloArray: array [0 .. 2] of TBitmap;

procedure MyLoadHello;
begin
  LHelloArray[0] := TBitmap.Create;
  LHelloArray[1] := TBitmap.Create;
  LHelloArray[2] := TBitmap.Create;
  ///
  LHelloArray[0].LoadFromFile('..\..\Hello.bmp');
  LHelloArray[1].LoadFromFile('..\..\Hello_Left.bmp');
  LHelloArray[2].LoadFromFile('..\..\Hello_Right.bmp');
end;

procedure MyDestroyHello;
begin
  for var i: integer := 0 to high(LHelloArray) do
    FreeAndNil(LHelloArray[i]);
end;

procedure MoveMyHello(const AKey: Char; const AImage: TImage);
var
  ALeft, ATop: integer;
begin
  if (AKey = '') or (AImage = nil) then
    exit;
  //
  ALeft := AImage.Left;
  ATop  := AImage.Top;
  //
  case AKey of
    'a', 'q', 'z', 'A', 'Q', 'Z':
      AImage.Picture.Bitmap := LHelloArray[1]; // left
    'd', 'e', 'c', 'D', 'E', 'C':
      AImage.Picture.Bitmap := LHelloArray[2]; // right
  else
    AImage.Picture.Bitmap := LHelloArray[0]; // default
  end;
  //
  case AKey of
    'a', 'A':
      ALeft := ALeft - LStep;
    'd', 'D':
      ALeft := ALeft + LStep;
    'w', 'W':
      ATop := ATop - LStep;
    's', 'S':
      ATop := ATop + LStep;
    'q', 'Q':
      begin
        ALeft := ALeft - LStep;
        ATop  := ATop - LStep;
      end;
    'e', 'E':
      begin
        ALeft := ALeft + LStep;
        ATop  := ATop - LStep;
      end;
    'z', 'Z':
      begin
        ALeft := ALeft - LStep;
        ATop  := ATop + LStep;
      end;
    'c', 'C':
      begin
        ALeft := ALeft + LStep;
        ATop  := ATop + LStep;
      end;
  end;
  //
  AImage.Left := ALeft;
  AImage.Top  := ATop;
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  Self.KeyPreview := true; // all keys processed by form...
  //
  ImageBackground.Picture.LoadFromFile('..\..\background.bmp');
  //
  MyLoadHello;
  //
  Image1.Stretch        := true;
  Image1.Transparent    := true; // to BMPs
  Image1.Picture.Bitmap := LHelloArray[0];
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
  MyDestroyHello;
end;

procedure TForm1.FormKeyPress(Sender: TObject; var Key: Char);
begin
  MoveMyHello(Key, Image1);
end;

end.

 

Project1_OTKJfzXcke.gif

Edited by programmerdelphi2k

Share this post


Link to post
4 hours ago, Lajos Juhász said:

For me this is working with VCL. I set the timer to 250ms.

You forgot to add an OnKeyUp event handler to reset the direction.

procedure TForm1.FormKeyDown(Sender: TObject;
  var Key: Word; Shift: TShiftState);
begin
  case Key of
    Ord('W'): fdirection := diup;
    Ord('A'): fDirection := diLeft;
    Ord('D'): fDirection := diRight;
    Ord('X'): fDirection := diDown;
  end;
end;

procedure TForm1.FormKeyUp(Sender: TObject;
  var Key: Word; Shift: TShiftState);
begin
  case Key of
    Ord('W'), Ord('A'), Ord('D'), Ord('X'):
      fDirection := diNone;
  end;
end;

 

Edited by Remy Lebeau
  • Like 1

Share this post


Link to post
On 3/17/2023 at 8:44 AM, Willicious said:

@GaryThanks for the suggestion, but tbh I'd like to try doing it as a Windows app. Later, I'll try again following the tutorial as you've suggested, for sure.

 

I believe the difference here is whether you're using VCL -- which is ONLY Windows -- or FMX -- of which Windows is ONE OF MANY target platforms that can be selected.

 

That is, with Windows, you can use EITHER VCL or FMX. 

Edited by David Schwartz
  • Like 1

Share this post


Link to post
7 hours ago, Lars Fosdal said:

It handles Unicode correctly.

Can you explain how? The help clearly states CharInSet checks whether a given character is contained within a set of AnsiChar.

Share this post


Link to post
9 hours ago, Lajos Juhász said:

Can you explain how?

The input char is mapped correctly.  For keyboards with national keys (f.x. äöæøå etc in Nordic), it at least works correctly.

Not sure how it translates source code to AnsiChar, considering that source code can be UTF-8.  


 

procedure TestChar;
var
 ch: char;
begin
  ch := '€';
  if ch in ['æ', 'ø', '€']  // gives [dcc32 Warning] : W1050 WideChar reduced to byte char in set expressions.  Consider using 'CharInSet' function in 'SysUtils' unit.
  then DebugOut('Yup');
end;

 

 

 

Share this post


Link to post

You should open the Sysutils and verify the magic. A spoiler alert. There is no black magic it uses the same code as when you write ch in [.... just without a warning. When you use CharInSet you just admit that you're aware what's going on (that you string is converted to AnsiString and char into ansichar).

Edited by Lajos Juhász

Share this post


Link to post

Interesting.  Did they have other plans once upon a time?  

This means it would be safer with if/then/else structures when testing for Unicode chars.

 

or use 

var
 ch: char;
begin
  ch := '€';
  if System.WideStrUtils.InOpArray(ch, ['æ', 'ø', '€'])
  then DebugOut('Yup');

but that is just a sequenctial scan of a widechar array.

Share this post


Link to post
35 minutes ago, Lars Fosdal said:

Interesting.  Did they have other plans once upon a time?  

This means it would be safer with if/then/else structures when testing for Unicode chars.

They couldn't. Set is limited to 255 elements, so CharInSet only works for Ansi sets (I'm sure you know that 😄 )

 

Btw, I wonder how do you check whether a char is a letter of a some language (f.ex. Norwegian, German)? They have not only latin chars but some additional which can't be declared with simple 'a'..'z'. You have to add them explicitly, like 'a'..'z', 'æ'?

Edited by Fr0sT.Brutal

Share this post


Link to post

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×