Results 1 to 10 of 31

Thread: Collission detection on level ( source Travels doc + some other examples)

Hybrid View

Previous Post Previous Post   Next Post Next Post
  1. #1
    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...

    Code:
    unit Main;
    interface
    uses
      Windows,Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
      Dialogs, AdDraws, AdClasses, AdTypes, AdPerformanceCounter, AdDevIL,AdSprites,Math;
      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 }
        AdDraw:TAdDraw;
        AdSpriteEngine:TSpriteEngine;
        AdImageList:TAdImageList;
        AdPerCounter:TAdPerformanceCounter;
        AdPixelCollisionTester: TAdSpritePixelCollisionTester;
        Blurp:TBlurp;
        procedure Idle(Sender:TObject; var Done:boolean);
        procedure LoadLevel;
        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}
    procedure TForm1.LoadLevel;
    var
      level:TStringList;
      ax,ay:integer;
    begin
      level := TStringList.Create;
      level.LoadFromFile(ExtractFilePath(Application.ExeName)+'level2.txt');
      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;
              with TPlatform.Create(AdSpriteEngine) do
              begin
                Image := AdImageList.Find('Kocka');
                x := ax*64;
                y := ay*64;
                z := 0;
              end;
            end;
            'X':
            begin
              TileInfo[ax,ay].iswall:=true;
              with TPlatForm.Create(AdSpriteEngine) do
              begin
                Image := AdImageList.Find('Kocka2');
                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
      AdPerCounter := TAdPerformanceCounter.Create;
      AdDraw := TAdDraw.Create(self);
      AdDraw.DllName := 'AndorraDX93D.dll';
    
    if AdDraw.Initialize then
      begin
        Application.OnIdle := Idle;
        AdImageList := TAdImageList.Create(AdDraw);
        AdImageList.LoadFromFile('BackGround.ail');
    
        //create the SpriteEngine
         AdSpriteEngine := TSpriteEngine.Create(nil);
         AdSpriteEngine.Surface := AdDraw;
        //Create the collision tester
        AdPixelCollisionTester := TAdSpritePixelCollisionTester.Create(AdDraw);
        LoadLevel;
        //create TImageSprite
        Randomize;
            Blurp := TBlurp.Create(AdSpriteEngine);
            with Blurp.Create(AdSpriteEngine) do
              begin
                Image := AdImageList.Find('Hero');
                X := 128;
                Y := 128;
                Z := 0;
                CollisionTester := AdPixelCollisionTester;
              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
        AdPerCounter.Calculate;
        AdPerCounter.MaximumFrameRate:=60;
    
        AdDraw.ClearSurface(clBlack); // Fill the surface with black color
        AdDraw.BeginScene;
        // Here you need to perform all drawing operations later
        ActTime := ActTime + AdPerCounter.TimeGap;
        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;
    
        AdSpriteEngine.Draw;
        AdSpriteEngine.Move(25/1000);
        AdSpriteEngine.Dead;
        // debug
        cnrs:=GetCorners(Blurp);
        AdDraw.Canvas.Textout(0,40,'Up left:    '+booltostr(cnrs.WallUL,true));
           AdDraw.Canvas.Textout(0,60,'Up right:    '+booltostr(cnrs.WallUR,true));
               AdDraw.Canvas.Textout(0,80,'Bottom left:    '+booltostr(cnrs.WallDL,true));
                   AdDraw.Canvas.Textout(0,100,'Bottom right:    '+booltostr(cnrs.WallDR,true));
        AdDraw.Canvas.TextOut(0,120,'Tile Tipus:' +inttostr(szam));
     
     
        AdDraw.EndScene;
        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
       AdSpriteEngine.Free;
       AdImageList.Free;
       AdPerCounter.Free;
       AdDraw.Free;
    end;
    procedure TForm1.FormKeyDown(Sender: TObject; var Key: Word;
      Shift: TShiftState);
    begin
      if Key = VK_ESCAPE then Close;
    end;
     
    end.

  2. #2
    I just noticed here:

    Code:
    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:

    Code:
    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):

    Code:
    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

  3. #3
    There is also this tutorial which uses the object corners and tile indices too
    http://www.metanetsoftware.com/technique/tutorialB.html

    cheers,
    Paul

  4. #4
    Hello

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


    Code:
        ActTime := ActTime + AdPerCounter.TimeGap;
        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...

    Code:
    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...

    Code:
    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.

    test.zip

    Greetings
    Rob

  5. #5
    I DID IT !!!!

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

    Code:
       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...
    Code:
       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
    Last edited by robert83; 18-08-2011 at 03:33 PM. Reason: is vx and vy set correctly?

  6. #6
    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.

  7. #7
    Quote Originally Posted by robert83 View Post
    I DID IT !!!!

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

    Code:
       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:

    Code:
       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

  8. #8
    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
    Code:
    function TBlurp.testForWall(spriteXpos,spriteYpos:single;spriteWidth,spriteHeight:integer;direction:string):boolean;
    begin
      testForWall := false;
      if direction = 'left' then
        begin
          if not (TileInfo[round(spriteXpos) div 64, round(spriteYpos) div 64].tilenr in [2]) or not
          (TileInfo[round(spriteXpos)div 64, (round(spriteYpos)+spriteHeight) div 64].tilenr in [2]) then testForWall := true;
        end;
      if direction = 'right' then
        begin
          if not(TileInfo[(round(spriteXpos)+spriteWidth) div 64, round(spriteYpos) div 64].tilenr in [2]) or not
          (TileInfo[(round(spriteXpos)+spriteWidth) div 64, (round(spriteYpos)+spriteHeight) div 64].tilenr in [2]) then testForWall :=true;
        end;
      if direction = 'up' then
        begin
          if not (TileInfo[round(spriteXpos) div 64, round(spriteYpos) div 64].tilenr in [2]) or not
          (TileInfo[(round(spriteXpos)+spriteWidth) div 64, round(spriteYpos) div 64].tilenr in [2]) then testForWall :=true;
        end;
      if direction = 'down' then
        begin
          if not (TileInfo[round(spriteXpos)div 64, (round(spriteYpos)+spriteHeight) div 64].tilenr in [2]) or not
          (TileInfo[(round(spriteXpos)+spriteWidth) div 64, (round(spriteYpos)+spriteHeight) div 64].tilenr in [2]) then testForWall :=true;
        end;
    end;
    controlling of character
    Code:
        ActTime := ActTime + AdPerCounter.TimeGap;
        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;
                      AdSpriteEngine.X:=AdSpriteEngine.X+4;
                    end;
                end;
              if GetKeyState(VK_RIGHT) < 0 then
                begin
                  if not Blurp.testForWall(Blurp.X,Blurp.Y,Blurp.Image.Width,Blurp.Image.Height-1,'right') then
                    begin
                      Blurp.X:=Blurp.X+4;
                      AdSpriteEngine.X:=AdSpriteEngine.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;
                      AdSpriteEngine.Y:=AdSpriteEngine.Y+4;
                    end;
                end;
              if GetKeyState(VK_DOWN) < 0 then
                begin
                  if not Blurp.testForWall(Blurp.X,Blurp.Y,Blurp.Image.Width-1,Blurp.Image.Height,'down') then
                    begin
                      Blurp.Y:=Blurp.Y+4;
                      AdSpriteEngine.Y:=AdSpriteEngine.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).

    egyszeru 7 5-os maskepp.zip

    Greetings
    Rob

Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •