0

I'd like to make a shape jump when the player presses the UP key, so the best i could think of is this, but the method i used is terrible and problematic:

(shape coordinates: shape1.top:=432;)

procedure TForm1.FormKeyDown(Sender: TObject; var Key: Word;
  Shift: TShiftState);
begin
case key of
vk_up: shape1.top:=shape1.top-40   //so that it jumps to 392
end; 
end;

And now this timer:

procedure TForm1.Timer1Timer(Sender: TObject);
begin
timer1.interval:=300
if shape1.Top<400 then      //if shape1.top=392 < 400
begin
shape1.Top:=432;            //move back to 432
end;

end;

The problem is that players can constantly press the key UP, which I don't want. I know this method is terrible, so i hope you have something better than this and i would be grateful if you could share it with me.

Bhargav Rao
  • 50,140
  • 28
  • 121
  • 140
user2296565
  • 243
  • 1
  • 7
  • 18
  • You are writing a game. The VCL is almost certainly the wrong library. You should use a game library. This will have a loop that runs constantly and will not use event driven programming style. You are probably using the wrong language too. – David Heffernan May 08 '13 at 17:02
  • 2
    @David: Maybe it is not intended to be GTA VI, so Delphi will work well if you use DirectX or OpenGL. If you do not need very fancy graphics, old-fashioned GDI will do the trick. – Andreas Rejbrand May 08 '13 at 17:06
  • 1
    @AndreasRejbrand It's not so much the graphics, more that event driven is not the norm for game programming. Game programming uses a main game loop and async keyboard handling. I think Mason has written a game programming library for Delphi. I'd probably write a game with pygame. – David Heffernan May 08 '13 at 17:07

2 Answers2

6

Here's a ball bouncing in a constant force field (e.g., the field of gravity close to the surface of the Earth). The lateral walls and the floor are bouncing surfaces. You can add additional forces using the arrow keys:

unit Unit5;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, ExtCtrls;

type
  TRealVect = record
    X, Y: real;
  end;

const
  ZeroVect: TRealVect = (X: 0; Y: 0);

type
  TForm5 = class(TForm)
    Timer1: TTimer;
    procedure FormPaint(Sender: TObject);
    procedure Timer1Timer(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure FormKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
    procedure FormKeyUp(Sender: TObject; var Key: Word; Shift: TShiftState);
  private
    { Private declarations }
    function ACC: TRealVect;
  const
    RADIUS = 16;
    DAMPING = 0.8;
    DT = 0.2;
    GRAVITY: TRealVect = (X: 0; Y: 10);
  var
    FForce: TRealVect;
    FPos: TRealVect;
    FVel: TRealVect;
  public
    { Public declarations }
  end;

var
  Form5: TForm5;

implementation

{$R *.dfm}

function RealVect(X, Y: real): TRealVect;
begin
  result.X := X;
  result.Y := Y;
end;

function Add(A, B: TRealVect): TRealVect;
begin
  result.X := A.X + B.X;
  result.Y := A.Y + B.Y;
end;

function Scale(A: TRealVect; C: real): TRealVect;
begin
  result.X := C*A.X;
  result.Y := C*A.Y;
end;

function TForm5.ACC: TRealVect;
begin
  result := Add(GRAVITY, FForce);
end;

procedure TForm5.FormCreate(Sender: TObject);
begin
  FPos := RealVect(Width div 2, 10);
  FVel := RealVect(0, 0);
end;

procedure TForm5.FormKeyDown(Sender: TObject; var Key: Word;
  Shift: TShiftState);
begin
  case Key of
    VK_UP:
      FForce := RealVect(0, -20);
    VK_DOWN:
      FForce := RealVect(0, 10);
    VK_RIGHT:
      FForce := RealVect(10, 0);
    VK_LEFT:
      FForce := RealVect(-10, 0);
  end;
end;

procedure TForm5.FormKeyUp(Sender: TObject; var Key: Word; Shift: TShiftState);
begin
  FForce := ZeroVect;
end;

procedure TForm5.FormPaint(Sender: TObject);
begin
  Canvas.Brush.Color := clRed;
  Canvas.Ellipse(round(FPos.X - RADIUS), round(FPos.Y - RADIUS),
    round(FPos.X + RADIUS), round(FPos.Y + RADIUS));
end;

procedure TForm5.Timer1Timer(Sender: TObject);
begin
  FVel := Add(FVel, Scale(ACC, DT));
  FPos := Add(FPos, Scale(FVel, DT));
  if FPos.Y + RADIUS >= ClientHeight then
  begin
    FVel.Y := -DAMPING*FVel.Y;
    FPos.Y := ClientHeight - RADIUS - 1;
  end;
  if FPos.X - RADIUS <= 0 then
  begin
    FVel.X := -DAMPING*FVel.X;
    FPos.X := RADIUS + 1;
  end;
  if FPos.X + RADIUS >= ClientWidth then
  begin
    FVel.X := -DAMPING*FVel.X;
    FPos.X := ClientWidth - RADIUS - 1;
  end;
  Invalidate;
end;

end.

Set the timer's interval to 30, as 'usual'.

Compiled sample EXE

Andreas Rejbrand
  • 105,602
  • 8
  • 282
  • 384
  • 1
    Forward Euler. You really need a nice vector class with operator overloading. – David Heffernan May 08 '13 at 17:06
  • @David: I have such a library. But IIRC, the OP is using Delphi 7 where operator overloading isn't possible. Anyhow, this answer is only meant to be a very simple exercise for beginning programmers, so I don't want to bloat it too much. – Andreas Rejbrand May 08 '13 at 17:07
  • Well, if this is an exercise for begginner programmers, then i should really give up programming – user2296565 May 08 '13 at 17:29
  • @user2296565: No, you shoudln't. I think you could get all of the code above if you just study it carefully for a while, assuming, of course, that you are familiar with simple classical mechanics (displacement, velocity, acceleration, force, Newton's laws, ...). – Andreas Rejbrand May 08 '13 at 17:31
  • well, i am excellent in science, i am terrible programmer though, always trying to do things that are for advanced programmers, like games – user2296565 May 08 '13 at 17:34
  • @user2296565: Maybe would be better to start off by doing some simple things, just to learn the concepts? – Andreas Rejbrand May 08 '13 at 17:35
  • Well, this is also what my teacher said, but making games is really what i like – user2296565 May 08 '13 at 17:39
  • @user2296565: Then my first advice to you is to study my code snippet above until you get it. Then try to modify it and add new features. Experiment. – Andreas Rejbrand May 08 '13 at 17:40
  • Thanks, i will try...do you want me to post it after adding new feautures? – user2296565 May 08 '13 at 17:47
  • @user2296565: No, I don't think that would fit on SO. But if you get any *specific* problems with some addition, then, of course, you can post that as a Q. – Andreas Rejbrand May 08 '13 at 17:48
  • 1
    If you want to make games, don't push shapes around vcl forms with timers. You'll write epic amounts of code to produce feeble effects. Use pygame or some such. If it must be Delphi (probably a bad choice for games) then use a game library at least. Don't use vcl. – David Heffernan May 08 '13 at 18:21
  • @user2296565, @David: I agree (to some extent) with David. I have said many times before that you should never animate by moving controls (even graphics ones, like `TShape`s) around. Instead, you should paint manually, either using old-fashioned GDI or using modern OpenGL or Direct X. Notice that my approach above uses manual drawing with GDI. – Andreas Rejbrand May 08 '13 at 18:23
  • I also Agree with both of you, but i have created many games using multiple programs, such as gamemaker, which was extremely easy to use and won many competitions, they also wanted me to teach little children how to do them :p, but making using a programming language such as delphi and c++ is a real challenge – user2296565 May 08 '13 at 20:22
  • and also, what library should i use, mr heffernan? Anything you know can help me, thanks – user2296565 May 08 '13 at 20:23
  • 2
    +1 for going way overboard. Not that with 49k you really need it :) – David May 08 '13 at 21:52
  • 2
    @user2296565 There is a number of great libraries for making games. Delphi is **not** a bad choice for making games. Besides, GameMaker was written in Delphi. I would strongly suggest you to visit [pascal game development site](http://www.pascalgamedevelopment.com/) and ask further questions there. As for libraries: I've used [Asphyre](http://www.afterwarp.net/) with good results. – Wodzu May 09 '13 at 07:21
3

If the player can hold down a key and KeyDown fires repeatedly, you can lock it.

First, declare a field on the form called FKeyLock: set of byte. (Note: this technique will fail if you get any Key values higher than 255, but the ones you're likely to deal with won't be that high.)

procedure TForm1.FormKeyDown(Sender: TObject; var Key: Word;
  Shift: TShiftState);
begin
  if key in FKeyLock then
    Exit;
  case key of
    vk_up:
    begin
      shape1.top:=shape1.top-40;   //so that it jumps to 392
      include(FKeyLock, vk_up);
    end;
  end;
end;

procedure TForm1.FormKeyUp(Sender: TObject; var Key: Word;
  Shift: TShiftState);
begin
   exclude(FKeyLock, key);
end;
Mason Wheeler
  • 82,511
  • 50
  • 270
  • 477