PDA

View Full Version : Collission detection on level ( source Travels doc + some other examples)

robert83
14-08-2011, 10:17 PM
Hello everyone,

This weekend I've managed to get a bit further on my road to making some platform games in the future...

Anyway using Traveler's guide for platform game + some andorra 2d examples... I've managed to create a simple level , load it, and my hero ( the big bad red block ) can move around and hits the wall... only problem is I dunno why, but sometimes he does not exactly hits it but stops a bit earlier...

I'm including my complete source + attached the zipped program so you can see what I mean, I've tried to implement my own method of detecting the edges, by defining the 4 points of the block, not much different from the original guide, but I understand this one atleast ...

unit Main;
interface
uses
Windows,Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
type
//Types that are used to tell the character which key has been pressed
TKey = (kyUp, kyDown, kyLeft, kyRight);
TKeys = set of TKey;
TTileInfo = Record
tilenr : integer;
end;
TPlatform = class(TImageSprite)
private
protected
procedure DoMove(TimeGap: Double); override;
procedure DoCollision(Sprite:TSprite; var Done:boolean); override;
public
constructor Create(AParent:TSprite);override;
end;
TBlurp = class(TImageSprite)
private
FKeys:TKeys;
X_Direction:double;
Y_Direction:double;
isfalling:boolean;
iswall:boolean;
protected
procedure DoMove(TimeGap: Double);override;
procedure DoCollision(Sprite:TSprite; var Done:boolean);override;
public
constructor Create(AParent:Tsprite);override;
procedure SetKeys(Keys:TKeys);
function testForWall(spriteXpos,spriteYpos:single;spriteWid th,spriteHeight:integer):boolean;
end;
TForm1 = class(TForm)
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
procedure FormKeyDown(Sender: TObject; var Key: Word;
Shift: TShiftState);
private
{ Private declarations }
public
{ Public declarations }
Blurp:TBlurp;
procedure Idle(Sender:TObject; var Done:boolean);
end;
var
Form1: TForm1;
TileInfo : array [1..39, 0..28 ] of TTileInfo;
ActTime : double;
implementation
{\$R *.dfm}
var
level:TStringList;
ax,ay:integer;
begin
level := TStringList.Create;
for ay := 0 to level.Count-1 do
begin
for ax := 1 to length(level[ay]) do
begin
case level[ay][ax] of
'x':
begin
TileInfo[ax,ay].tilenr:=1;
begin
x := ax*64;
y := ay*64;
z := 0;
end;
end;
'X':
begin
TileInfo[ax,ay].tilenr:=1;
begin
x := ax*64;
y := ay*64;
z := 0;
end;
end;
'-': TileInfo[ax,ay].tilenr:=2;
end;
end;
end;
level.Free;
end;
procedure TBlurp.SetKeys(Keys: TKeys);
begin
if Keys = FKeys then exit;
FKeys := Keys;
if kyRight in Keys then x_direction:=1.5;
if kyLeft in Keys then x_direction:=-1.5;
if kyUp in Keys then y_direction:=-1.5;
if kyDown in Keys then y_direction:=1.5;
if Keys = [] then
begin
x_direction:=0;
y_direction:=0;
end;
end;

constructor TPlatform.Create(AParent: TSprite);
begin
inherited;
end;
constructor TBlurp.Create(AParent: TSprite);
begin
inherited;
end;

procedure TBlurp.DoMove(TimeGap: Double);
begin
inherited;
{ X:=X+X_direction;
Y:=Y+Y_direction; }

{ if X <= 0 then X:=0;
if X > 640-Image.Width then X:=640-Image.Width;
if Y < 0 then Y:=0;
if Y > 480-Image.Height then Y:=480-Image.Height; }
Collision;
end;
function TBlurp.testForWall(spriteXpos,spriteYpos:single;sp riteWidth,spriteHeight:integer):boolean;
begin
testForWall := false;
if not (TileInfo[round(spriteXpos) div 64, round(spriteYpos) div 64].tilenr in ) or not
(TileInfo[(round(spriteXpos)+spriteWidth) div 64, round(spriteYpos) div 64].tilenr in ) or not
(TileInfo[round(spriteXpos)div 64, (round(spriteYpos)+spriteHeight) div 64].tilenr in ) or not
(TileInfo[(round(spriteXpos)+spriteWidth) div 64, (round(spriteYpos)+spriteHeight) div 64].tilenr in ) then testForWall :=true;
end;
procedure TPlatform.DoMove(TimeGap : Double);
begin
inherited;
Collision;
end;

procedure TBlurp.DoCollision(Sprite:TSprite; var Done:boolean);
begin
if Sprite is TPlatform then
begin
isFalling:=false;
end;
end;
procedure TPlatform.DoCollision(Sprite:TSprite; var Done:boolean);
begin
end;

procedure TForm1.FormCreate(Sender: TObject);
var i: integer;
begin
begin
{ Width := 640;
Height := 480;
DisplayMode := dmFullscreen; }
end;

begin
Application.OnIdle := Idle;

//create the SpriteEngine
//Create the collision tester
//create TImageSprite
Randomize;
begin
X := 128;
Y := 128;
Z := 0;
SetKeys([]);
isfalling:=true;
end;

// ************* hater *********************************
end
else
begin
ShowMessage('Error while initializing Andorra 2D. Try to use another display'+
'mode or use another video adapter.');
halt; //<-- Completely shuts down the application
end;
end;
procedure TForm1.Idle(Sender: TObject; var Done: boolean);
var keys: TKeys;
begin
if AdDraw.CanDraw then // Only continue, if drawing is possible
begin

AdDraw.ClearSurface(clBlack); // Fill the surface with black color
// Here you need to perform all drawing operations later
if ActTime > 25 then
begin
keys := [];
if GetKeyState(VK_LEFT) < 0 then
begin
if not Blurp.testForWall(Blurp.X-3,Blurp.Y,Blurp.Image.Width,Blurp.Image.Height) then
begin
Blurp.X:=Blurp.X-3;
end;
end;
if GetKeyState(VK_RIGHT) < 0 then
begin
if not Blurp.testForWall(Blurp.X+3,Blurp.Y,Blurp.Image.Wi dth,Blurp.Image.Height) then
begin
Blurp.X:=Blurp.X+3;
end;
end;
if GetKeyState(VK_UP) < 0 then
begin
if not Blurp.testForWall(Blurp.X,Blurp.Y-3,Blurp.Image.Width,Blurp.Image.Height) then
begin
Blurp.Y:=Blurp.Y-3;
end;
end;
if GetKeyState(VK_DOWN) < 0 then
begin
if not Blurp.testForWall(Blurp.X,Blurp.Y+3,Blurp.Image.Wi dth,Blurp.Image.Height) then
begin
Blurp.Y:=Blurp.Y+3;
end;
end;
Blurp.SetKeys(Keys);
ActTime := 0;
end;
{ if Blurp.isfalling = true then
begin
Blurp.Y:=Blurp.Y+1;
end;
Blurp.isfalling:=true;}

// debug

AdDraw.Flip; // Draws you result on the screen. Otherwise, you would not see anything
end;
Done := false; // Important, otherwise the function will not be called in every loop
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
end;
procedure TForm1.FormKeyDown(Sender: TObject; var Key: Word;
Shift: TShiftState);
begin
if Key = VK_ESCAPE then Close;
end;
end.

this worked :

function TOurSprite.testForWall(spriteXpos,spriteYpos:singl e;
spriteWidth,spriteHeight:integer):boolean;
begin
testForWall := false;
//test topside
if not (TileInfo[((round(spriteXpos)+spriteWidth) div 32),
(round(spriteYpos) div 32)].tilenr in [6,7,100])
//test bottomside
or not ((TileInfo[((round(spriteXpos)+spriteWidth) div 32),
((round(spriteYpos)+spriteHeight) div 32)].tilenr in [6,7,100])
and (TileInfo[(round(spriteXpos) div 32),
((round(spriteYpos)+spriteHeight) div 32)].tilenr in [6,7,100]))
then testForWall := true;

only thing was I was not able to figure it out how and why :) I was able to figure out that
it was checking , these coordinates in this order

X------1
| -------|
3------2 Why was it not checking 4 x,y have no idea... it worked... but I did not want to use
something I don't understand...

Anyway back to my way I defined all 4 points
X----X
| ------|
X----X

only problem is sometimes my Hero Character stops a bit earlier then it should...
I must stop him a bit sooner otherwise... I get wallhit true...and cannot move in any direction...

Please help me out a bit here ( I try to do as much as possible by myself...but I'm really stuck here...)

540

Greetings

Robert

paul_nicholls
15-08-2011, 06:24 AM
Maybe this site can help?
http://www.tonypa.pri.ee/tbw/tut05.html

It is using action script (Flash) but I managed to folow the tutorials, especially "Hit the Wall" and "Jumping" to make a simple platform game with collision that worked quite well :)

cheers,
Paul

User137
15-08-2011, 02:33 PM
function TBlurp.testForWall(spriteXpos,spriteYpos:single;sp riteWidth,spriteHeight:integer):boolean;
begin
testForWall := false;
if not (TileInfo[round(spriteXpos) div 64, round(spriteYpos) div 64].tilenr in ) or not
(TileInfo[(round(spriteXpos)+spriteWidth) div 64, round(spriteYpos) div 64].tilenr in ) or not
(TileInfo[round(spriteXpos)div 64, (round(spriteYpos)+spriteHeight) div 64].tilenr in ) or not
(TileInfo[(round(spriteXpos)+spriteWidth) div 64, (round(spriteYpos)+spriteHeight) div 64].tilenr in ) then testForWall :=true;
end;
Test with changing round() to trunc(). There is a difference in how they work, and tiles are usually drawn from their top left corner, not from middle.

robert83
15-08-2011, 04:11 PM
Hi,

same thing with Trunc...

I don't get this : "and tiles are usually drawn from their top left corner, not from middle."
- why did you write this ? am I doing something wrong when checking the 4 corners?

By the way... if I use it like this :

if not Blurp.testForWall(Blurp.X,Blurp.Y,Blurp.Image.Widt h,Blurp.Image.Height) then

for any direction... it stops when it hits the wall... but since it HIT the wall I can no longer
move in any direction... that is why I did that -3 here +3 here... I'm a bit confused there... I should check if after the next step there is a wall...

Greetings
Rob

Traveler
15-08-2011, 09:55 PM
There are two ways to counter the-getting-stuck-in-the-wall problem.

1) Move away from the wall when you're inside it. (the preferred method)
For example, you're moving to the left at 3 pixel/cycle. You're going to hit the wall in 11px. Meaning you'll end up in the wall (and get stuck) in 4 cycles. At the fourth cycle your position is 1px inside the wall, so you correct that by setting the sprite position 2 pixels back.

2) Change the testwall function to include the direction. Meaning when you go to the right you only test if the right side of your sprite is inside a wall. If you're going left, only test if you with with the left part. This way you can still move if one side of your sprite is inside a wall.

paul_nicholls
15-08-2011, 10:21 PM
The tutorial site I posted seems to do the testing like this:

find new object position (position + velocity).
if edge inside a wall
move object so it touches edge of wall
else
do full movement in that direction

Oh, and it breaks up the movement and does it vertically then horizontally (or is it the other way around...I forget LOL)

I hope this helps :)

cheers,
Paul

paul_nicholls
15-08-2011, 10:24 PM
@Robert: hmm...I tried running your test program to take a look, but it doesn't run for me :(

I get the dialog box error below:

---------------------------
Dreamworld: DreamWorld.exe - Unable To Locate Component
---------------------------
This application has failed to start because d3dx9_33.dll was not found. Re-installing the application may fix this problem.
---------------------------
OK
---------------------------

cheers,
Paul

Traveler
16-08-2011, 08:10 AM

paul_nicholls
16-08-2011, 09:11 AM
ok, thanks :)

cheers,
Paul

robert83
16-08-2011, 01:15 PM
Hello,

I've changed the movement from 3 to 4 now, so that it can be divided correctly... since my level is made of 64x64 tiles ...

I think the problem is here but don't know how to fix....

Left and Top works always on my level everywhere... it stops right there when it reaches the wall (no pixel left out...) , and I can continue to move in any other direction ...

Left = works
Top = works
Right = stops 4 pixels sooner then it should...always
Bottom = stops 4 pixels sooner then it should...always

meanwhile I've also found out that it's important where my character starts... so I've set the starting position to

X (3x64)-32
Y (3x64)-32

I hope I'm not wrong here, but currently my tiles are 64x64, whereas my character is 32x32.

I've checked the Right and Bottom testwall functions... it works as it should :) because it is right that if it lets another +4 to x or +4 to y ... it is true... only thing is I can't seem to firugre it out... how the heck does it work correctly for Left and Top...

if GetKeyState(VK_LEFT) < 0 then
begin
if not Blurp.testForWall(Blurp.X-4,Blurp.Y,Blurp.Image.Width,Blurp.Image.Height) then
begin
Blurp.X:=Blurp.X-4;
end;
end;
if GetKeyState(VK_RIGHT) < 0 then
begin
if not Blurp.testForWall(Blurp.X+4,Blurp.Y,Blurp.Image.Wi dth,Blurp.Image.Height) then
begin
Blurp.X:=Blurp.X+4;
end;
end;
if GetKeyState(VK_UP) < 0 then
begin
if not Blurp.testForWall(Blurp.X,Blurp.Y-4,Blurp.Image.Width,Blurp.Image.Height) then
begin
Blurp.Y:=Blurp.Y-4;
end;
end;
if GetKeyState(VK_DOWN) < 0 then
begin
if not Blurp.testForWall(Blurp.X,Blurp.Y+4,Blurp.Image.Wi dth,Blurp.Image.Height) then
begin
Blurp.Y:=Blurp.Y+4;
end;

function TBlurp.testForWall(spriteXpos,spriteYpos:single;sp riteWidth,spriteHeight:integer):boolean;
begin
testForWall := false;
if not (TileInfo[round(spriteXpos) div 64, round(spriteYpos) div 64].tilenr in ) or not
(TileInfo[(round(spriteXpos)+spriteWidth) div 64, round(spriteYpos) div 64].tilenr in ) or not
(TileInfo[round(spriteXpos)div 64, (round(spriteYpos)+spriteHeight) div 64].tilenr in ) or not
(TileInfo[(round(spriteXpos)+spriteWidth) div 64, (round(spriteYpos)+spriteHeight) div 64].tilenr in ) then testForWall :=true;
end;

Greetings
Rob

User137
16-08-2011, 03:56 PM
I'll throw out this kind of suggestion:

if GetKeyState(VK_LEFT) < 0 then
begin
if not Blurp.testForWall(Blurp.X-4,Blurp.Y) then
begin
Blurp.X:=Blurp.X-4;
end;
end;
if GetKeyState(VK_RIGHT) < 0 then
begin
if not Blurp.testForWall(Blurp.X+4+Blurp.Image.Width,Blur p.Y) then
begin
Blurp.X:=Blurp.X+4;
end;
end;
if GetKeyState(VK_UP) < 0 then
begin
if not Blurp.testForWall(Blurp.X,Blurp.Y-4) then
begin
Blurp.Y:=Blurp.Y-4;
end;
end;
if GetKeyState(VK_DOWN) < 0 then
begin
if not Blurp.testForWall(Blurp.X,Blurp.Y+4+Blurp.Image.He ight) then
begin
Blurp.Y:=Blurp.Y+4;
end;
end;

function TBlurp.testForWall(pointX,pointY:single):boolean;
var tilenr: integer;
begin
tilenr:=TileInfo[trunc(pointX) div 64, trunc(pointY) div 64].tilenr;
case tilenr of
2: result:=true;
else result:=false;
end;
end;

robert83
16-08-2011, 05:07 PM
Thank you! It's so simple and works wonderfully... I had to modify it a bit :

Right :

if GetKeyState(VK_RIGHT) < 0 then
begin
if not Blurp.testForWall(Blurp.X+Blurp.Image.Width,Blurp. Y) then
begin
Blurp.testForWall(Blurp.X+4+Blurp.Image.Width,Blur p.Y);
Blurp.X:=Blurp.X+4;
end;
end;

Down

if GetKeyState(VK_DOWN) < 0 then
begin
if not Blurp.testForWall(Blurp.X,Blurp.Y+Blurp.Image.Heig ht) then
begin
Blurp.testForWall(Blurp.X,Blurp.Y+4+Blurp.Image.He ight);
Blurp.Y:=Blurp.Y+4;
end;
end;

removed +4 otherwise it stopped 4 pixels sooner as before to the right and bottom, now it stops when it hits the wall and I can move in any other direction ... GREAT!

Only thing is there are two bugs, which I'll look into tonight for some reason the following is possible :

557

If I approach the tile from bellow, and hit it... keep pressing UP...and start moving to the
left... the moment a small part of my character sprite is "free" it allows me to go up and
down inside the wall :) ( I'm still happy tough, this is a better BUG atleast :) )
Right side is not affected by this... if I find another wall sprite, and keep pressing top
and try to go right...I can only go up once my character is completely free.

556

Same here, if I approach a Wall sprite from left or right, and my Character is a bit Higher ... it can go trough...and move Left and Right... if I do the same with my character being lover then the Wall sprite it is not happening...

Think this has something to do with only using X,Y maybe... anyway I'll play with it tonight.

Attached is my modified "demo" thing.

Thank you again for "spoon feeding" this to me, thank you VERY VERY much!!!
555

Greetings
Robert

paul_nicholls
16-08-2011, 09:05 PM
Hey Robert, have you looked at that tile game tutorial site I posted earlier? That might really help you get this all working :)

Maybe this site can help?
http://www.tonypa.pri.ee/tbw/tut05.html

It is using action script (Flash) but I managed to folow the tutorials, especially "Hit the Wall" and "Jumping" to make a simple platform game with collision that worked quite well :)

cheers,
Paul

paul_nicholls
17-08-2011, 04:52 AM
Hey Robert, I have whipped up some code (NOT tested) based on that tutorial if you want to study it?

// based on tutorial here:
// http://www.tonypa.pri.ee/tbw/tut05.html
uses
Math;

const
cTileW = 32; // tile width
cTileH = 32; // tile height

cMapW = 20; // map width in tiles
cMapH = 20; // map height in tiles

type
TTile = record
Id : Integer; // tile Id - not applicaple in this exercise
IsWall : Boolean; // True = wall, False = no wall
end;

TSprite = record
w,h : Integer; //half object width and height in pixels
x,y : Single; //current location (centre of object)
vx,vy : Single; //current velocity in pixels/second
end;

TCorners = record
Up : Integer; // object's top edge map tile index
Down : Integer; // object's bottom edge map tile index
Left : Integer; // object's left edge map tile index
Right : Integer; // object's right edge map tile index

WallUL : Boolean; // is or isn't a wall at object's top left corner
WallUR : Boolean; // is or isn't a wall at object's top right corner
WallDL : Boolean; // is or isn't a wall at object's bottom left corner
WallDR : Boolean; // is or isn't a wall at object's bottom right corner
end;

var
Map: array[0..cMapH - 1,0..cMapW - 1] of TTile;

function WallAt(x,y: Integer): Boolean;
// tests for the presence of a wall at a tile location
begin
Result := True;

if (x < 0) or (x >= cMapW) then Exit; // returns true if location is out of bounds (safety reasons, object can't pass out of bounds)
if (y < 0) or (y >= cMapH) then Exit; // returns true if location is out of bounds (safety reasons, object can't pass out of bounds)

Result := Map[x,y].IsWall;
end;

function GetCorners(aSpr: TSprite): TCorners;
// get object corners, includes velocity!
begin
// calculate tile index for each object corner
Result.Left := Math.Floor((aSpr.x - aSpr.w + aSpr.vx) / cTileW);
Result.Right := Math.Floor((aSpr.x + aSpr.w + aSpr.vx) / cTileW);
Result.Up := Math.Floor((aSpr.y - aSpr.h + aSpr.vy) / cTileH);
Result.Down := Math.Floor((aSpr.y + aSpr.h + aSpr.vy) / cTileH);

// if true then there is a wall there
Result.WallUL := WallAt(Result.Left ,Result.Up);
Result.WallUR := WallAt(Result.Right ,Result.Up);
Result.WallDL := WallAt(Result.Left ,Result.Down);
Result.WallDR := WallAt(Result.Right ,Result.Down);
end;

procedure MoveSprite(var aSpr: TSprite; aTimeDelta: Single);
var
cnrs: TCorners;
begin
cnrs := GetCorners(aSpr);

if aSpr.vy < 0 then
// moving up
begin
if not cnrs.WallUL and not cnrs.WallUR then
// not obstructed so move normally
aSpr.y := aSpr.y + aSpr.vy * aTimeDelta
else
// hit tile so move to edge of tile and stop
begin
aSpr.vy := 0;
aSpr.y := cnrs.Up * cTileH + cTileH + aSpr.h;
end;
end
else
if aSpr.vy > 0 then
// moving down
begin
if not cnrs.WallDL and not cnrs.WallDR then
// not obstructed so move normally
aSpr.y := aSpr.y + aSpr.vy * aTimeDelta
else
// hit tile so move to edge of tile and stop
begin
aSpr.vy := 0;
aSpr.y := cnrs.Down * cTileH - aSpr.h;
end;
end;

cnrs := GetCorners(aSpr);

if aSpr.vx < 0 then
// moving left
begin
if not cnrs.WallUL and not cnrs.WallDL then
// not obstructed so move normally
aSpr.x := aSpr.x + aSpr.vx * aTimeDelta
else
// hit tile so move to edge of tile and stop
begin
aSpr.vx := 0;
aSpr.x := cnrs.Left * cTileW + cTileW + aSpr.w;
end;
end
else
if aSpr.vx > 0 then
// moving right
begin
if not cnrs.WallUR and not cnrs.WallDR then
// not obstructed so move normally
aSpr.x := aSpr.x + aSpr.vx * aTimeDelta
else
// hit tile so move to edge of tile and stop
begin
aSpr.vx := 0;
aSpr.x := cnrs.Right * cTileW - aSpr.w;
end;
end;
end;

cheers,
Paul

robert83
17-08-2011, 06:58 AM
Hello,

thank you very much, I'll be doing some reading on that site you've posted the link to.

Thank you for the code as well!

Greetings
Rob

robert83
17-08-2011, 04:47 PM
Hello,

I've modified my program so that it now kinda does what Paul suggested...only a bit dumbed down...

I wanted to ask something, cause I think this might be the root cause of my problem, something I don't understand and need to make clear.

For example when my character hits a wall in any direction, I need to make him go back one pixel in the oposite direction ? in order to prevent it from being stuck ?

Right now in my program where tiles are 64x64, character is 32x32, and movement in any direction is by 4 pixels always, when I hit the wall (perfect hit) I get stuck... for example I hit the wall from left... upleft and downleft is TRUE... I should be able to move up and down but I can't because for UP and DOWN I check upleft upright, and downleft downright, and one of em is always true... so do I need to move my character back 1 pixel when I colide with a wall? I'm not sure...cause on that Flash tutorial link, the character thing seems to hit the wall perfectly and yet it's able to move and hit the wall from left for example perfectly and yet is still able to move up and down along the wall.

my code :

unit Main;
interface
uses
Windows,Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
type
//Types that are used to tell the character which key has been pressed
TKey = (kyUp, kyDown, kyLeft, kyRight);
TKeys = set of TKey;
TTileInfo = Record
iswall : boolean;
end;
TCorners = record
Up : Integer; // object's top edge map tile index
Down : Integer; // object's bottom edge map tile index
Left : Integer; // object's left edge map tile index
Right : Integer; // object's right edge map tile index
WallUL : Boolean; // is or isn't a wall at object's top left corner
WallUR : Boolean; // is or isn't a wall at object's top right corner
WallDL : Boolean; // is or isn't a wall at object's bottom left corner
WallDR : Boolean; // is or isn't a wall at object's bottom right corner
end;

TPlatform = class(TImageSprite)
private
protected
procedure DoMove(TimeGap: Double); override;
procedure DoCollision(Sprite:TSprite; var Done:boolean); override;
public
constructor Create(AParent:TSprite);override;
end;
TBlurp = class(TImageSprite)
private
FKeys:TKeys;
X_Direction:double;
Y_Direction:double;
isfalling:boolean;
iswall:boolean;
protected
procedure DoMove(TimeGap: Double);override;
procedure DoCollision(Sprite:TSprite; var Done:boolean);override;
public
constructor Create(AParent:Tsprite);override;
procedure SetKeys(Keys:TKeys);
end;
TForm1 = class(TForm)
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
procedure FormKeyDown(Sender: TObject; var Key: Word;
Shift: TShiftState);
private
{ Private declarations }
public
{ Public declarations }
Blurp:TBlurp;
procedure Idle(Sender:TObject; var Done:boolean);
function GetCorners(aSpr: TSprite): TCorners;
function WallAt(x,y: Integer): Boolean;
end;
var
Form1: TForm1;
TileInfo : array [1..39, 0..28 ] of TTileInfo;
ActTime : double;
szam : integer;
implementation
{\$R *.dfm}
var
level:TStringList;
ax,ay:integer;
begin
level := TStringList.Create;
for ay := 0 to level.Count-1 do
begin
for ax := 1 to length(level[ay]) do
begin
case level[ay][ax] of
'x':
begin
TileInfo[ax,ay].iswall:=true;
begin
x := ax*64;
y := ay*64;
z := 0;
end;
end;
'X':
begin
TileInfo[ax,ay].iswall:=true;
begin
x := ax*64;
y := ay*64;
z := 0;
end;
end;
'-': TileInfo[ax,ay].iswall:=false;
end;
end;
end;
level.Free;
end;
procedure TBlurp.SetKeys(Keys: TKeys);
begin
if Keys = FKeys then exit;
FKeys := Keys;
if kyRight in Keys then x_direction:=1.5;
if kyLeft in Keys then x_direction:=-1.5;
if kyUp in Keys then y_direction:=-1.5;
if kyDown in Keys then y_direction:=1.5;
if Keys = [] then
begin
x_direction:=0;
y_direction:=0;
end;
end;

constructor TPlatform.Create(AParent: TSprite);
begin
inherited;
end;
constructor TBlurp.Create(AParent: TSprite);
begin
inherited;
end;

procedure TBlurp.DoMove(TimeGap: Double);
begin
inherited;

{ X:=X+X_direction;
Y:=Y+Y_direction; }

{ if X <= 0 then X:=0;
if X > 640-Image.Width then X:=640-Image.Width;
if Y < 0 then Y:=0;
if Y > 480-Image.Height then Y:=480-Image.Height; }
Collision;
end;
function TForm1.WallAt(x,y: Integer): Boolean;
// tests for the presence of a wall at a tile location
begin
Result := True;
Result := TileInfo[x,y].IsWall;
end;
function TForm1.GetCorners(aSpr: TSprite): TCorners;
// get object corners, includes velocity!
begin
// calculate tile index for each object corner
Result.Left := Math.Floor((aSpr.x -4) / 64);
Result.Right := Math.Floor((aSpr.x + aSpr.Width) / 64);
Result.Up := Math.Floor((aSpr.y -4) / 64);
Result.Down := Math.Floor((aSpr.y + aSpr.Height) / 64);
// if true then there is a wall there
Result.WallUL := WallAt(Result.Left ,Result.Up);
Result.WallUR := WallAt(Result.Right ,Result.Up);
Result.WallDL := WallAt(Result.Left ,Result.Down);
Result.WallDR := WallAt(Result.Right ,Result.Down);
end;

procedure TPlatform.DoMove(TimeGap : Double);
begin
inherited;
Collision;
end;

procedure TBlurp.DoCollision(Sprite:TSprite; var Done:boolean);
begin
if Sprite is TPlatform then
begin
isFalling:=false;
end;
end;
procedure TPlatform.DoCollision(Sprite:TSprite; var Done:boolean);
begin
end;

procedure TForm1.FormCreate(Sender: TObject);
var i: integer;
begin
begin
{ Width := 640;
Height := 480;
DisplayMode := dmFullscreen; }
end;

begin
Application.OnIdle := Idle;

//create the SpriteEngine
//Create the collision tester
//create TImageSprite
Randomize;
begin
X := 128;
Y := 128;
Z := 0;
SetKeys([]);
isfalling:=true;
end;

// ************* hater *********************************
end
else
begin
ShowMessage('Error while initializing Andorra 2D. Try to use another display'+
'mode or use another video adapter.');
halt; //<-- Completely shuts down the application
end;
end;
procedure TForm1.Idle(Sender: TObject; var Done: boolean);
var keys: TKeys;
cnrs: TCorners;
begin
if AdDraw.CanDraw then // Only continue, if drawing is possible
begin

AdDraw.ClearSurface(clBlack); // Fill the surface with black color
// Here you need to perform all drawing operations later
if ActTime > 25 then
begin
cnrs := GetCorners(Blurp);
keys := [];
if GetKeyState(VK_LEFT) < 0 then
begin
if not cnrs.WallUL and not cnrs.WallDL then
begin
Blurp.X:=Blurp.X-4;
end;
end;
if GetKeyState(VK_RIGHT) < 0 then
begin
if not cnrs.WallUR and not cnrs.WallDR then
begin
Blurp.X:=Blurp.X+4;
end;
end;
if GetKeyState(VK_UP) < 0 then
begin
if not cnrs.WallUL and not cnrs.WallUR then
begin
Blurp.y := Blurp.y - 4;
end;
end;
if GetKeyState(VK_DOWN) < 0 then
begin
if not cnrs.WallDL and not cnrs.WallDR then
begin
Blurp.Y:=Blurp.Y+4;
end;
end;
Blurp.SetKeys(Keys);
ActTime := 0;
end;
{ if Blurp.isfalling = true then
begin
Blurp.Y:=Blurp.Y+1;
end;
Blurp.isfalling:=true;}

// debug

AdDraw.Flip; // Draws you result on the screen. Otherwise, you would not see anything
end;
Done := false; // Important, otherwise the function will not be called in every loop
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
end;
procedure TForm1.FormKeyDown(Sender: TObject; var Key: Word;
Shift: TShiftState);
begin
if Key = VK_ESCAPE then Close;
end;

end.

and zipped is the entire project... 559

greetings
Rob

User137
17-08-2011, 07:00 PM
The collision depends very much on how the player is allowed to move. For example if you would let player to move freely in 360 degrees you'd have a very different collision algorithm, than if player can only move vertically or horizontally.

There is not 1 way to make collision but 1000 different. For example:
1) Before movement, save character's full position in temp variable. If player collisions, simply copy the old position to current position.
Problem: If tile width is 32 and player is allowed to move in odd steps of 3.0812 for example, then player will never actually touch the wall but collide little outside different on each wall. Solution, move player by number that is divisible to 32.

2) For each 4 directions (up, down, left, right), if player is moving down and hits wall:
- Move player backwards by as much as he moves per frame.
or
- Count where player should be when it is exactly outside of collision range. For example if player is moving right and hits left wall, then PlayerX := WallX-PlayerSprite.Width

paul_nicholls
17-08-2011, 09:08 PM
You have to do the collision test BEFORE actually moving the character - this is what the checking corners part of the code is all about; only if you can move in that direction (not blocked by wall), do you then move there - otherwise you move to the wall edge.

I'm having trouble following your code a bit, but it seems you are moving then testing for collisions?

procedure TBlurp.DoMove(TimeGap: Double);
begin
inherited;

{ X:=X+X_direction;
Y:=Y+Y_direction; }

{ if X <= 0 then X:=0;
if X > 640-Image.Width then X:=640-Image.Width;
if Y < 0 then Y:=0;
if Y > 480-Image.Height then Y:=480-Image.Height; }
Collision;
end;

And why are you moving 4 pixels each time? Doesn't this mean you are not going to get frame-independent movement in your game?
I would have thought you should move by your velocity (4 pixels?) times by the frame time (TimeGap)?

cheers,
Paul

robert83
17-08-2011, 10:55 PM
Hello,

Erm...I think I'm doing the movement incorrectly... I've cleaned up my code a bit, I'm doing the movement in the IDLE loop of my Form... every

if ActTime > 25 then

I've limited the max framerate to 60, and by pressing any of the direction keys I move the Player.X or Y position with +4 or -4 to any direction...

If this is a really bad habit, do tell me, I'm still trying to learn understand... and there are parts I still don't get completely. Andorra 2d's Wormhunter demo control scheme was a bit overwhelming at first look.

As you can see...I'm not doing the movement in the Sprite.OnMove procedure... (sorry... I've only used Andorra 2d for about two weeks... and that was not non-stop..., it a lot of fun, but sometimes a bit overwhelming, but not giving up...)

Complete code :

unit Main;
interface
uses
Windows,Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
type
TTileInfo = Record
iswall : boolean;
end;
TCorners = record
Up : Integer; // object's top edge map tile index
Down : Integer; // object's bottom edge map tile index
Left : Integer; // object's left edge map tile index
Right : Integer; // object's right edge map tile index
WallUL : Boolean; // is or isn't a wall at object's top left corner
WallUR : Boolean; // is or isn't a wall at object's top right corner
WallDL : Boolean; // is or isn't a wall at object's bottom left corner
WallDR : Boolean; // is or isn't a wall at object's bottom right corner
end;

TPlatform = class(TImageSprite)
private
protected
procedure DoMove(TimeGap: Double); override;
procedure DoCollision(Sprite:TSprite; var Done:boolean); override;
public
constructor Create(AParent:TSprite);override;
end;
TBlurp = class(TImageSprite)
private
protected
procedure DoMove(TimeGap: Double);override;
procedure DoCollision(Sprite:TSprite; var Done:boolean);override;
public
constructor Create(AParent:Tsprite);override;
end;
TForm1 = class(TForm)
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
procedure FormKeyDown(Sender: TObject; var Key: Word;
Shift: TShiftState);
private
{ Private declarations }
public
{ Public declarations }
Blurp:TBlurp;
procedure Idle(Sender:TObject; var Done:boolean);
function GetCorners(aSpr: TSprite): TCorners;
function WallAt(x,y: Integer): Boolean;
end;
var
Form1: TForm1;
TileInfo : array [1..39, 0..28 ] of TTileInfo;
ActTime : double;
szam : integer;
implementation
{\$R *.dfm}
var
level:TStringList;
ax,ay:integer;
begin
level := TStringList.Create;
for ay := 0 to level.Count-1 do
begin
for ax := 1 to length(level[ay]) do
begin
case level[ay][ax] of
'x':
begin
TileInfo[ax,ay].iswall:=true;
begin
x := ax*64;
y := ay*64;
z := 0;
end;
end;
'X':
begin
TileInfo[ax,ay].iswall:=true;
begin
x := ax*64;
y := ay*64;
z := 0;
end;
end;
'-': TileInfo[ax,ay].iswall:=false;
end;
end;
end;
level.Free;
end;

constructor TPlatform.Create(AParent: TSprite);
begin
inherited;
end;
constructor TBlurp.Create(AParent: TSprite);
begin
inherited;
end;

procedure TBlurp.DoMove(TimeGap: Double);
begin
inherited;
Collision;
end;
function TForm1.WallAt(x,y: Integer): Boolean;
// tests for the presence of a wall at a tile location
begin
Result := True;
Result := TileInfo[x,y].IsWall;
end;
function TForm1.GetCorners(aSpr: TSprite): TCorners;
// get object corners, includes velocity!
begin
// calculate tile index for each object corner
Result.Left := Math.Floor((aSpr.x -4) / 64);
Result.Right := Math.Floor((aSpr.x + aSpr.Width) / 64);
Result.Up := Math.Floor((aSpr.y -4) / 64);
Result.Down := Math.Floor((aSpr.y + aSpr.Height) / 64);
// if true then there is a wall there
Result.WallUL := WallAt(Result.Left ,Result.Up);
Result.WallUR := WallAt(Result.Right ,Result.Up);
Result.WallDL := WallAt(Result.Left ,Result.Down);
Result.WallDR := WallAt(Result.Right ,Result.Down);
end;

procedure TPlatform.DoMove(TimeGap : Double);
begin
inherited;
Collision;
end;

procedure TBlurp.DoCollision(Sprite:TSprite; var Done:boolean);
begin
end;
procedure TPlatform.DoCollision(Sprite:TSprite; var Done:boolean);
begin
end;

procedure TForm1.FormCreate(Sender: TObject);
var i: integer;
begin

begin
Application.OnIdle := Idle;

//create the SpriteEngine
//Create the collision tester
//create TImageSprite
Randomize;
begin
X := 128;
Y := 128;
Z := 0;
end;

// ************* hater *********************************
end
else
begin
ShowMessage('Error while initializing Andorra 2D. Try to use another display'+
'mode or use another video adapter.');
halt; //<-- Completely shuts down the application
end;
end;
procedure TForm1.Idle(Sender: TObject; var Done: boolean);
var cnrs: TCorners;
szar: boolean;
begin
if AdDraw.CanDraw then // Only continue, if drawing is possible
begin

AdDraw.ClearSurface(clBlack); // Fill the surface with black color
// Here you need to perform all drawing operations later
if ActTime > 25 then
begin
cnrs := GetCorners(Blurp);
if GetKeyState(VK_LEFT) < 0 then
begin
if not cnrs.WallUL and not cnrs.WallDL then
begin
Blurp.X:=Blurp.X-4;
end;
end;
if GetKeyState(VK_RIGHT) < 0 then
begin
if not cnrs.WallUR and not cnrs.WallDR then
begin
Blurp.X:=Blurp.X+4;
end;
end;
if GetKeyState(VK_UP) < 0 then
begin
if not cnrs.WallUL and not cnrs.WallUR then
begin
Blurp.y := Blurp.y - 4;
end;
end;
if GetKeyState(VK_DOWN) < 0 then
begin
if not cnrs.WallDL and not cnrs.WallDR then
begin
Blurp.Y:=Blurp.Y+4;
end;
end;

ActTime := 0;
end;

// debug

AdDraw.Flip; // Draws you result on the screen. Otherwise, you would not see anything
end;
Done := false; // Important, otherwise the function will not be called in every loop
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
end;
procedure TForm1.FormKeyDown(Sender: TObject; var Key: Word;
Shift: TShiftState);
begin
if Key = VK_ESCAPE then Close;
end;

end.

robert83
17-08-2011, 11:35 PM
Can't sleep :)

Changed my code...I think this is a bit more proper.... now I'm using the OnMove procedure.... it's a bit buggy ...cause it still stops a bit sooner then it should...and I'm stuck as well.... but I think this is the proper way to move my character...

unit Main;
interface
uses
Windows,Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
type
TTileInfo = Record
iswall : boolean;
end;
TCorners = record
Up : Integer; // object's top edge map tile index
Down : Integer; // object's bottom edge map tile index
Left : Integer; // object's left edge map tile index
Right : Integer; // object's right edge map tile index
WallUL : Boolean; // is or isn't a wall at object's top left corner
WallUR : Boolean; // is or isn't a wall at object's top right corner
WallDL : Boolean; // is or isn't a wall at object's bottom left corner
WallDR : Boolean; // is or isn't a wall at object's bottom right corner
end;

TPlatform = class(TImageSprite)
private
protected
procedure DoMove(TimeGap: Double); override;
procedure DoCollision(Sprite:TSprite; var Done:boolean); override;
public
constructor Create(AParent:TSprite);override;
end;
TBlurp = class(TImageSprite)
private
vx,vy : Single; //current velocity in pixels/second
protected
procedure DoMove(TimeGap: Double);override;
procedure DoCollision(Sprite:TSprite; var Done:boolean);override;
public
constructor Create(AParent:Tsprite);override;
end;
TForm1 = class(TForm)
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
procedure FormKeyDown(Sender: TObject; var Key: Word;
Shift: TShiftState);
private
{ Private declarations }
public
{ Public declarations }
Blurp:TBlurp;
procedure Idle(Sender:TObject; var Done:boolean);
function GetCorners(aSpr: TSprite): TCorners;
function WallAt(x,y: Integer): Boolean;
end;
var
Form1: TForm1;
TileInfo : array [1..39, 0..28 ] of TTileInfo;
ActTime : double;
szam : integer;
implementation
{\$R *.dfm}
var
level:TStringList;
ax,ay:integer;
begin
level := TStringList.Create;
for ay := 0 to level.Count-1 do
begin
for ax := 1 to length(level[ay]) do
begin
case level[ay][ax] of
'x':
begin
TileInfo[ax,ay].iswall:=true;
begin
x := ax*64;
y := ay*64;
z := 0;
end;
end;
'X':
begin
TileInfo[ax,ay].iswall:=true;
begin
x := ax*64;
y := ay*64;
z := 0;
end;
end;
'-': TileInfo[ax,ay].iswall:=false;
end;
end;
end;
level.Free;
end;

constructor TPlatform.Create(AParent: TSprite);
begin
inherited;
end;
constructor TBlurp.Create(AParent: TSprite);
begin
inherited;
end;

procedure TBlurp.DoMove(TimeGap: Double);
var
cnrs: TCorners;
begin
inherited;
cnrs := Form1.GetCorners(self);
if vy < 0 then
// moving up
begin
if not cnrs.WallUL and not cnrs.WallUR then
// not obstructed so move normally
y := y + vy * TimeGap
else
// hit tile so move to edge of tile and stop
begin
vy := 0;
// y := cnrs.Up * 64 + 64 {+ Height};
end;
end
else
if vy > 0 then
// moving down
begin
if not cnrs.WallDL and not cnrs.WallDR then
// not obstructed so move normally
y := y + vy * TimeGap
else
// hit tile so move to edge of tile and stop
begin
vy := 0;
// y := cnrs.Down * 64 - Height;
end;
end;
cnrs := Form1.GetCorners(self);
if vx < 0 then
// moving left
begin
if not cnrs.WallUL and not cnrs.WallDL then
// not obstructed so move normally
x := x + vx * TimeGap
else
// hit tile so move to edge of tile and stop
begin
vx := 0;
// x := cnrs.Left * 64 + 64 + Width;
end;
end
else
if vx > 0 then
// moving right
begin
if not cnrs.WallUR and not cnrs.WallDR then
// not obstructed so move normally
x := x + vx * TimeGap
else
// hit tile so move to edge of tile and stop
begin
vx := 0;
// x := cnrs.Right * 64 - Width;
end;
end;

// Collision;
end;
function TForm1.WallAt(x,y: Integer): Boolean;
// tests for the presence of a wall at a tile location
begin
Result := True;
Result := TileInfo[x,y].IsWall;
end;
function TForm1.GetCorners(aSpr: TSprite): TCorners;
// get object corners, includes velocity!
begin
// calculate tile index for each object corner
Result.Left := Math.Floor((aSpr.x -4) / 64);
Result.Right := Math.Floor((aSpr.x + aSpr.Width) / 64);
Result.Up := Math.Floor((aSpr.y -4) / 64);
Result.Down := Math.Floor((aSpr.y + aSpr.Height) / 64);
// if true then there is a wall there
Result.WallUL := WallAt(Result.Left ,Result.Up);
Result.WallUR := WallAt(Result.Right ,Result.Up);
Result.WallDL := WallAt(Result.Left ,Result.Down);
Result.WallDR := WallAt(Result.Right ,Result.Down);
end;

procedure TPlatform.DoMove(TimeGap : Double);
begin
inherited;
Collision;
end;

procedure TBlurp.DoCollision(Sprite:TSprite; var Done:boolean);
begin
end;
procedure TPlatform.DoCollision(Sprite:TSprite; var Done:boolean);
begin
end;

procedure TForm1.FormCreate(Sender: TObject);
var i: integer;
begin

begin
Application.OnIdle := Idle;

//create the SpriteEngine
//Create the collision tester
//create TImageSprite
Randomize;
begin
X := 128;
Y := 128;
Z := 0;
end;

// ************* hater *********************************
end
else
begin
ShowMessage('Error while initializing Andorra 2D. Try to use another display'+
'mode or use another video adapter.');
halt; //<-- Completely shuts down the application
end;
end;
procedure TForm1.Idle(Sender: TObject; var Done: boolean);
var cnrs: TCorners;
szar: boolean;
begin
if AdDraw.CanDraw then // Only continue, if drawing is possible
begin

AdDraw.ClearSurface(clBlack); // Fill the surface with black color
// Here you need to perform all drawing operations later
if ActTime > 100 then
begin
Blurp.vx:=0;
Blurp.vy:=0;
if GetKeyState(VK_LEFT) < 0 then
begin
Blurp.vx:=-14;
end;
if GetKeyState(VK_RIGHT) < 0 then
begin
Blurp.vx:=+14;
end;
if GetKeyState(VK_UP) < 0 then
begin
Blurp.vy:=-14
end;
if GetKeyState(VK_DOWN) < 0 then
begin
Blurp.vy:=14
end;

ActTime := 0;
end;

// debug
cnrs:=GetCorners(Blurp);

AdDraw.Flip; // Draws you result on the screen. Otherwise, you would not see anything
end;
Done := false; // Important, otherwise the function will not be called in every loop
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
end;
procedure TForm1.FormKeyDown(Sender: TObject; var Key: Word;
Shift: TShiftState);
begin
if Key = VK_ESCAPE then Close;
end;

end.

paul_nicholls
18-08-2011, 12:45 AM
I just noticed here:

function TForm1.GetCorners(aSpr: TSprite): TCorners;
// get object corners, includes velocity!
begin
// calculate tile index for each object corner
Result.Left := Math.Floor((aSpr.x -4) / 64);
Result.Right := Math.Floor((aSpr.x + aSpr.Width) / 64);
Result.Up := Math.Floor((aSpr.y -4) / 64);
Result.Down := Math.Floor((aSpr.y + aSpr.Height) / 64);

this seems incorrect...I think it should be this:

function TForm1.GetCorners(aSpr: TSprite): TCorners;
// get object corners, includes velocity!
begin
// calculate tile index for each object corner
Result.Left := Math.Floor((aSpr.x - aSpr.w + aSpr.vx) / cTileW);
Result.Right := Math.Floor((aSpr.x + aSpr.w + aSpr.vx) / cTileW);
Result.Up := Math.Floor((aSpr.y - aSpr.h + aSpr.vy) / cTileH);
Result.Down := Math.Floor((aSpr.y + aSpr.h + aSpr.vy) / cTileH);

Each sprite corner is the position (x or y) added to the sprite's half width or height (see ascii art):

TL up TR
* | *
|-h
Left | Right
--------*-------
-w | +w
|+h
|
* down *
BL BR

This then needs to have the velocity added onto it so the correct tile index is being calculated for the new position being moved to :)

Does this make it any clearer? :)

Oh, also I can't see where you are setting the sprite's vx an vy properties in the code?

Note: I think you should use constants or similar for the tile width and height so it is all the same throughout the code and only needs to be changed once :)

PS. Maybe I will download Adorra so I can try fixing the code myself if you are still having troubles :)

Good luck :)

cheers.
Paul

paul_nicholls
18-08-2011, 12:55 AM
There is also this tutorial which uses the object corners and tile indices too :)
http://www.metanetsoftware.com/technique/tutorialB.html

cheers,
Paul

robert83
18-08-2011, 09:38 AM
Hello

I'm manipulating the vx and vy on TForm1.OnIDLE like this :

if ActTime > 100 then
begin
Blurp.vx:=0;
Blurp.vy:=0;
if GetKeyState(VK_LEFT) < 0 then
begin
Blurp.vx:=-16;
end;
if GetKeyState(VK_RIGHT) < 0 then
begin
Blurp.vx:=+16;
end;
if GetKeyState(VK_UP) < 0 then
begin
Blurp.vy:=-16
end;
if GetKeyState(VK_DOWN) < 0 then
begin
Blurp.vy:=16
end;

ActTime := 0;
end;

The DoMOVE procedure for the Character object...

procedure TBlurp.DoMove(TimeGap: Double);
var
cnrs: TCorners;
begin
inherited;
cnrs := Form1.GetCorners(self);
if vy < 0 then
// moving up
begin
if (not cnrs.WallUL) and (not cnrs.WallUR) then
// not obstructed so move normally
y := y + vy * TimeGap
else
// hit tile so move to edge of tile and stop
begin
vy := 0;
y := cnrs.Up * cTileH + cTileH + 16;
end;
end
else
if vy > 0 then
// moving down
begin
if (not cnrs.WallDL) and (not cnrs.WallDR) then
// not obstructed so move normally
y := y + vy * TimeGap
else
// hit tile so move to edge of tile and stop
begin
vy := 0;
y := cnrs.Down * cTileH - 16;
end;
end;
cnrs := Form1.GetCorners(self);
if vx < 0 then
// moving left
begin
if (not cnrs.WallUL) and (not cnrs.WallDL) then
// not obstructed so move normally
x := x + vx * TimeGap
else
// hit tile so move to edge of tile and stop
begin
vx := 0;
x := cnrs.Left * cTileW + cTileW + 16;
end;
end
else
if vx > 0 then
// moving right
begin
if (not cnrs.WallUR) and (not cnrs.WallDR) then
// not obstructed so move normally
x := x + vx * TimeGap
else
// hit tile so move to edge of tile and stop
begin
vx := 0;
x := cnrs.Right * cTileW - 16;
end;
end;

// Collision;
end;

I've also modified the code for getCorners, for some reason I did not notice HALF heihgt and HALF width, since my character is 32x32, I'm using 16 here. This part now should be ok, thanks to your ASCII art now I understand this part...

function TForm1.GetCorners(aSpr: TSprite): TCorners;
// get object corners, includes velocity!
begin
// calculate tile index for each object corner
Result.Left := Math.Floor((aSpr.x -16 + Blurp.vx) / cTileW);
Result.Right := Math.Floor((aSpr.x + 16 + Blurp.vx) / cTileW);
Result.Up := Math.Floor((aSpr.y -16 + Blurp.vy) / cTileH);
Result.Down := Math.Floor((aSpr.y + 16 + Blurp.vy) / cTileH);
// if true then there is a wall there
Result.WallUL := WallAt(Result.Left ,Result.Up);
Result.WallUR := WallAt(Result.Right ,Result.Up);
Result.WallDL := WallAt(Result.Left ,Result.Down);
Result.WallDR := WallAt(Result.Right ,Result.Down);
end;

Problem : stops a bit further then it should LEFT and TOP, and to late RIGHT and BOTTOM, it's like the calulation is OK but Offset.

During testing I've commented out the lines that "should" put my character right beside the wall if there is a hit... because that to is working in an unexpected way.

Attached is my latest source code, meanwhile I'll play with it... hopefully I'm manipulating the vx and vy in a correct way.

560

Greetings
Rob

robert83
18-08-2011, 01:50 PM
I DID IT !!!!

Without your help i wouldn't get this far this soon... but it still feels kinda good :D

Result.Left := Math.Floor((x +(Image.Width/2) + vx) / cTileW);
Result.Right := Math.Floor((x + (Image.Width/2) + vx) / cTileW);
Result.Up := Math.Floor((y + (Image.Height/2) + vy) / cTileH);
Result.Down := Math.Floor((y + (Image.Height/2) + vy) / cTileH);

for some reason, and I will need to figure it out why... Left and UP works if instead of - I do + Image.Widht/2 and + Image.Height/2

Now I can go to the wall everywhere...there is one small bug... with any direction if I have a single WALL in center or anywhere...where I can move around it...
my character can anter this WALL from LEFT or RIGHT if my character is HIGHER or LOWER then this WALL by more then my Characters own Height/2.

C is character points , X is wall... can move inside wall... but only if I touch it like that... UP DOWN, LEFT and RIGHT works...

CC
CCXX
XXX
CCXX
CC

Same thing happens for UP and DOWN... my Character can enter the WALL from UP and DOWN if it's LEFT or RIGHT side is free by more then its Width/2.
(hopefully I've managed to write this down and someone understand's this... my english might be bad)

Regarding the vx and vy ... I've studied a few demos..samples... I know it works...now, but was wondering if I'm doing this the right way, the way it was meant to be done in Andorra 2d (or if this is not specific to just this engine... then any other engine).

Damn... was to soon to say... It seems if I change...vx or vy to other value... it does not work correctly...guess this only works cause I made a "lucky" bug

Greetings
Robert

User137
18-08-2011, 04:55 PM
You cannot simply change + and - anywhere when you like. Does it look logical? For that case Left and Up must be - . You can see this by looking at what the function returns. It returns the area where sprite is. If both were + it would result a simple dot that is located in down right corner of sprite, for + and - it is a rectangle all around player sprite what was wanted. So bugs are still elsewhere.

Also, you are using vx and vy varibles in GetCorners() function without multiplying it with TimeGap.

paul_nicholls
18-08-2011, 08:56 PM
I DID IT !!!!

Without your help i wouldn't get this far this soon... but it still feels kinda good :D

Result.Left := Math.Floor((x +(Image.Width/2) + vx) / cTileW);
Result.Right := Math.Floor((x + (Image.Width/2) + vx) / cTileW);
Result.Up := Math.Floor((y + (Image.Height/2) + vy) / cTileH);
Result.Down := Math.Floor((y + (Image.Height/2) + vy) / cTileH);

for some reason, and I will need to figure it out why... Left and UP works if instead of - I do + Image.Widht/2 and + Image.Height/2

That's great news :)

I realised I forgot to mention these facts which are kind of crucial :-[

With the code I gave you, the world coordinate system I was working has the origin at the top left of the screen, so:

left = -movement
right = +movement
up = -movement
down = +movement

so you need to match the +/- with your Adorra 2d screen coordinate system.

Since you said left and up now seem to work using + instead of - as in my example then I would guess you need to use this now:

Result.Left := Math.Floor((x +(Image.Width/2) + vx) / cTileW);
Result.Right := Math.Floor((x - (Image.Width/2) + vx) / cTileW);
Result.Up := Math.Floor((y + (Image.Height/2) + vy) / cTileH);
Result.Down := Math.Floor((y - (Image.Height/2) + vy) / cTileH);

does this change things?

cheers,
Paul

robert83
18-08-2011, 10:09 PM
Hello,

Thank you Paul for the suggestion... I guess in my confusion I confused you as well sorry... Andorra 2d also top left origin... anyway I was banging my head against the wall the whole day (probably overcomplicating something that is not that complex...) . I've decided I give a try to what Traveler said before... to include direction in my testforwall function. And this time (kinda like a wonder) it really works :

TestWall function

function TBlurp.testForWall(spriteXpos,spriteYpos:single;sp riteWidth,spriteHeight:integer;direction:string):b oolean;
begin
testForWall := false;
if direction = 'left' then
begin
if not (TileInfo[round(spriteXpos) div 64, round(spriteYpos) div 64].tilenr in ) or not
(TileInfo[round(spriteXpos)div 64, (round(spriteYpos)+spriteHeight) div 64].tilenr in ) then testForWall := true;
end;
if direction = 'right' then
begin
if not(TileInfo[(round(spriteXpos)+spriteWidth) div 64, round(spriteYpos) div 64].tilenr in ) or not
(TileInfo[(round(spriteXpos)+spriteWidth) div 64, (round(spriteYpos)+spriteHeight) div 64].tilenr in ) then testForWall :=true;
end;
if direction = 'up' then
begin
if not (TileInfo[round(spriteXpos) div 64, round(spriteYpos) div 64].tilenr in ) or not
(TileInfo[(round(spriteXpos)+spriteWidth) div 64, round(spriteYpos) div 64].tilenr in ) then testForWall :=true;
end;
if direction = 'down' then
begin
if not (TileInfo[round(spriteXpos)div 64, (round(spriteYpos)+spriteHeight) div 64].tilenr in ) or not
(TileInfo[(round(spriteXpos)+spriteWidth) div 64, (round(spriteYpos)+spriteHeight) div 64].tilenr in ) then testForWall :=true;
end;
end;

controlling of character

if ActTime > 25 then
begin
if GetKeyState(VK_LEFT) < 0 then
begin
if not Blurp.testForWall(Blurp.X-4,Blurp.Y,Blurp.Image.Width,Blurp.Image.Height-1,'left') then
begin
Blurp.X:=Blurp.X-4;
end;
end;
if GetKeyState(VK_RIGHT) < 0 then
begin
if not Blurp.testForWall(Blurp.X,Blurp.Y,Blurp.Image.Widt h,Blurp.Image.Height-1,'right') then
begin
Blurp.X:=Blurp.X+4;
end;
end;
if GetKeyState(VK_UP) < 0 then
begin
if not Blurp.testForWall(Blurp.X,Blurp.Y-4,Blurp.Image.Width-1,Blurp.Image.Height,'up') then
begin
Blurp.Y:=Blurp.Y-4;
end;
end;
if GetKeyState(VK_DOWN) < 0 then
begin
if not Blurp.testForWall(Blurp.X,Blurp.Y,Blurp.Image.Widt h-1,Blurp.Image.Height,'down') then
begin
Blurp.Y:=Blurp.Y+4;
end;
end;
ActTime := 0;
end;

Right now I'm doing the movement onIdle of TFORM ... which means I'm not using TimeGap for movement... which is bad I know, I just wanted to make it real simple for now (so that I can sleep well, that atleast I've acomplished half of what I wanted).... tomorrow I'll modify my code so that the movement is done in the Sprites.OnMove , and I only give x or y velocity here...

Anyway I've attached my code with the compiled executable, if I may ask you to please test it, it works. And I'm hoping it works just as well as the one you've suggested. (multiple solutions for the same problem).

561

Greetings
Rob

paul_nicholls
18-08-2011, 10:18 PM
It works for me just fine (after I downloaded the missing directx dll (d3dx9_33.dll)...the last version I tried needed the d3dx9_31.dll version for some reason? :)

I was thinking...if go for the opengl version dll for Andorra 2d, then your game (when finished) could be cross-platform...just a thought, and my \$0.02 :D

cheers,
Paul

robert83
20-08-2011, 11:15 PM
Hi,

I was playing a bit further with my program, and modified so I do the movement in the SpriteEngine.DoMove procedure , and it works fine regardles if velocity is 80, 95, or whatever... what I modified compared to my older source is :

TForm1.OnIDLE

Here is one obvious problem, which I did not even tried to solve... for now, somehow my Figure moves faster then the
screen is scroll. ( I was hoping the speed would be the same...but there is a slight difference... will play with it later... was not
the object here to solve that ... but rather to implement the movement more properly)

if ActTime > 25 then
begin
Blurp.vx:=0;
Blurp.vy:=0;
if GetKeyState(VK_LEFT) < 0 then
begin
Blurp.vx:=-95;
end;
if GetKeyState(VK_RIGHT) < 0 then
begin
Blurp.vx:=95;
end;
if GetKeyState(VK_UP) < 0 then
begin
Blurp.vy:=-95;
end;
if GetKeyState(VK_DOWN) < 0 then
begin
Blurp.vy:=95;
end;
ActTime := 0;
end;

Here Is my modified DoMove , for now works great, been playing with it , using different velocity... always stops where it should yippeee :)

procedure TBlurp.DoMove(TimeGap: Double);
begin
inherited;
// left
if vx < 0 then
begin
if not testForWall(X+vx*TimeGap,Y,Image.Width,Image.Heigh t-1,'left') then
begin
X:=X+round(vx*TimeGap) ;
end;
end;
// right
if vx > 0 then
begin
if not testForWall(X,Y,Image.Width,Image.Height-1,'right') then
begin
X:=X+round(vx*TimeGap);
end;
end;
// up
if vy < 0 then
begin
if not testForWall(X,Y+vy*TimeGap,Image.Width-1,Image.Height,'up') then
begin
Y:=Y+round(vy*TimeGap);
end;
end;
// down
if vy > 0 then
begin
if not testForWall(X,Y,Image.Width-1,Image.Height,'down') then
begin
Y:=Y+round(vy*TimeGap);
end;
end;

Collision;
end;

Also one unexpected sidefect is ... before it was important where my Start position for my character was... now it's not , X can be 128,139,131 ... it works the same.

Anyway I'll continue now with implementing gravity :) And checking the scrolling part.

Greetings
Rob

robert83
21-08-2011, 12:52 AM
if ActTime > 10 then
begin
Blurp.vx:=0;
Blurp.vy:=0;
if GetKeyState(VK_LEFT) < 0 then
begin
Blurp.vx:=-80;
if (Blurp.X > 320) and (Blurp.X < (40*64)-320) then
begin
end;
end;
if GetKeyState(VK_RIGHT) < 0 then
begin
Blurp.vx:=80;
if (Blurp.X > 320) and (Blurp.X < (40*64)-320) then
begin
end;
end;
if GetKeyState(VK_UP) < 0 then
begin
Blurp.vy:=-80;
if (Blurp.Y > 240) and (Blurp.Y < (29*64)-240) then
begin
end;
end;
if GetKeyState(VK_DOWN) < 0 then
begin
Blurp.vy:=80;
if (Blurp.Y > 240) and (Blurp.Y < (29*64)-240) then
begin
end;
end;
ActTime := 0;
end;

Due to the fact that I use 60 FPS limit... had to decrease ACT TIME to 10... I don't know what to call this effect but when ACT TIME was 60... the screen was moving... I dunnot a bit erm... tear effect ... the outer lines of my character seemed to be jumping in and out of the character when it was redrawn...

Anyway now it works great :D , and was pretty simple to do to. Only thing, gravity...tomorrow.

Greetings
Rob

ps. : once I'm finished with my experiments...I'll use const and more variables instead of manually putting in numbers....

paul_nicholls
21-08-2011, 05:04 AM
Hi Rob, glad to hear that it is now working for you :)

cheers,
Paul
{"ft_lang_label":"__label__en","ft_lang_prob":0.5486664,"math_prob":0.7525884,"size":64127,"snap":"2021-04-2021-17","text_gpt3_token_len":18701,"char_repetition_ratio":0.16510457,"word_repetition_ratio":0.52396023,"special_character_ratio":0.27866578,"punctuation_ratio":0.27624974,"nsfw_num_words":0,"has_unicode_error":false,"math_prob_llama3":0.95992756,"pos_list":[0],"im_url_duplicate_count":[null],"WARC_HEADER":"{\"WARC-Type\":\"response\",\"WARC-Date\":\"2021-01-20T13:42:21Z\",\"WARC-Record-ID\":\"<urn:uuid:8532f529-658b-4f42-b0eb-6094c708810d>\",\"Content-Length\":\"95943\",\"Content-Type\":\"application/http; msgtype=response\",\"WARC-Warcinfo-ID\":\"<urn:uuid:bcc733a4-6735-4cf6-82a2-fcfe4764b451>\",\"WARC-Concurrent-To\":\"<urn:uuid:bfaf5636-4448-4697-bd48-228167be9351>\",\"WARC-IP-Address\":\"78.129.197.31\",\"WARC-Target-URI\":\"https://www.pascalgamedevelopment.com/archive/index.php/t-10549.html?s=f97b13c30a2ace0d24557daa607fb41b\",\"WARC-Payload-Digest\":\"sha1:CHNPGZYRRQITPAE6O6G55OCNCD5CZWBI\",\"WARC-Block-Digest\":\"sha1:KG56GA6NNNGOD6DIJKIWRZBFKZ6LZCEC\",\"WARC-Identified-Payload-Type\":\"application/xhtml+xml\",\"warc_filename\":\"/cc_download/warc_2021/CC-MAIN-2021-04/CC-MAIN-2021-04_segments_1610703520883.15_warc_CC-MAIN-20210120120242-20210120150242-00589.warc.gz\"}"}