Results 1 to 10 of 10

Thread: Collision done directly with OnCollision ( don't understand it, help me understand)

  1. #1

    Collision done directly with OnCollision ( don't understand it, help me understand)

    Hello everyone,

    On my quest to find better (and more simple) ways to do collision I've stumbled upon this on a Asypre 2d tutorial, and quickly converted it to Andorra 2d ( did not knew before that I can get Sprite values from OnCollision ( example : TileLeft := Trunc(TPlatform(Sprite).X); )
    // though I was wondering why it was written all over the Andorra 2d tutorials that the sprite engine is a very powerfull thing and that you are capable of writing games with it, given if you know how

    Anyway it works, but not perfectly because I don't understand what these +4 -2 +5 etc are for... I've already changed parts of the original code which I was 100% sure in. The problem I'm having manifests itself by allowing my character to move inside tile if he's under it and he's head is just a bit higher then tile bottom, I can then move left and right in it.

    I've opened the resource file that comes with the original project, the HERO pattern size is 32x48 , the blocks that he can do collision with are 32x32 (block1) and 64x64 (block2)

    Part of the original code
    Code:
              TileLeft := Trunc(TTile(Sprite).X);
              TileTop := Trunc(TTile(Sprite).Y);
              TileRight := Trunc(TTile(Sprite).X) + TTile(Sprite).PatternWidth;
              TileBottom := Trunc(TTile(Sprite).Y) + TTile(Sprite).PatternHeight;
              if (TTile(Sprite).ImageName = 'Block1') or (TTile(Sprite).ImageName = 'Block2') then
    My modified onMove code
    Code:
     procedure TBlurp.DoMove(TimeGap: Double);
    begin
      inherited;
         Left :=   Round(X+2); // what is this +2 , same with +1 so it seems,no + not work
         Top :=    Round(Y+4); // what is this ?
         Right :=  Round(X + Image.Width);
         Bottom := Round(Y + Image.Height);
     
      // setting the camera to follow our HERO
      if (X > 320) and (X < (40*TileW)-320-TileW) then
        begin
          // - 64 , because when loading the level my entire background is starting from 64 instead of 0
          // looks better if there is no black line at the edge of the level...
          camera_x:=(X*-1)+320-TileW;
        end;
      if (Y > 240) and (Y < (29*TileH)-240) then
        begin
          // 29 tiles down * 64 tile.height -240 half of the screen, only moving when at center of the screen
          // well not really the center...but close anyway :)
          camera_y:=(Y*-1)+240;
        end;
     
        if GetKeyState(VK_LEFT) < 0 then
          begin
            vx:=123;
            x := x - vx * TimeGap;
          end;
        if GetKeyState(VK_RIGHT) < 0 then
          begin
            vx:=123;
            x := x + vx * TimeGap;
          end;
        if GetKeyState(VK_UP) < 0 then
          begin
            vy:=123;
            y := y - vy * TimeGap;
          end;
        if GetKeyState(VK_DOWN) < 0 then
          begin
            vy:=123;
            y := y + vy * TimeGap;
          end;
     
      Collision;
    end;
    The oncollision
    Code:
    procedure TBlurp.DoCollision(Sprite:TSprite; var Done:boolean);
    var
       TileLeft, TileRight,
       TileTop, TileBottom: Integer;
    begin
      if Sprite is TPlatform then
        begin
          TileLeft := Trunc(TPlatform(Sprite).X);
          TileTop := Trunc(TPlatform(Sprite).Y);
          TileRight := Trunc(TPlatform(Sprite).X) + TPlatform(Sprite).Image.Width;
          TileBottom := Trunc(TPlatform(Sprite).Y) + TPlatform(Sprite).Image.Height;
          // up
          if GetKeyState(VK_UP) < 0 then
            begin
              if (Self.Top+5  > TileBottom) and
                 (Self.Right-4  > TileLeft) and
                 (Self.Left+3  < TileRight) then
                 Y := TileBottom;
            end;
          // down
          if GetKeyState(VK_DOWN) < 0 then
            begin
              if (Self.Bottom -4  < TileTop )  and
                 (Self.Right -4   > TileLeft)  and
                 (Self.Left +3   < TileRight) then
                 Y := TileTop-60;
            end;
          // left
          if GetKeyState(VK_LEFT) < 0 then
            begin
              if (Self.Left+8 > TileRight) and
              (Self.Top+5< TileBottom) and
              (Self.Bottom-8>TileTop)  then
              X := TileRight;
            end;
          // right
          if GetKeyState(VK_RIGHT) < 0 then
            begin
              if (Self.Right-8 < TileLeft )and
              (Self.Top+5< TileBottom) and
              (Self.Bottom-8>TileTop)  then
              X := TileLeft -30;
            end;
        end;
    end;
    Again I don't understand these +5 -8 etc... what is happening here ?

    I've attached my source which is almos working correctly...
    And the original material

    Greetings
    Robert
    SpriteEngineRPGMap.zip
    unSimple 0.1.zip

  2. #2
    change the DoMove to
    Code:
         Left :=   Round(X); // what is this +2 , same with +1 so it seems,no + not work
         Top :=    Round(Y); // what is this ?
    cause you set +4 already in OnCollision, this means if you set X+2 here it means you test Left(wich is X) later with Left+2+4 instead of Left+4

    EDIT: meh stupid me, it should also work if you remove the +5, -8 it is only good if you have a sprite wich has transperant borders to match power of two, means a 24x56 sprite in a 32x64 texture
    so you would have to set all +/- to +4 and -4 to make the sprite collision correctly without space between tile and character
    Last edited by Daikrys; 28-08-2011 at 04:55 PM.

  3. #3
    Thank you,

    I've changed onMove to
    Code:
          Left :=   Round(X);
         Top :=    Round(Y);
         Right :=  Round(X + Image.Width);
         Bottom := Round(Y + Image.Height);
    It still works,

    but if I remove those +5,-8 it does not works for up,left,right

    I forgot to mention my hero sprite is 30x60... does that mean.. I have do to it like
    this ?
    Code:
          // up
          if GetKeyState(VK_UP) < 0 then
            begin
              if (Self.Top+4  > TileBottom) and
                 (Self.Right-2  > TileLeft) and
                 (Self.Left+2  < TileRight) then
                 Y := TileBottom;
            end;
          // down
          if GetKeyState(VK_DOWN) < 0 then
            begin
              if (Self.Bottom -4  < TileTop )  and
                 (Self.Right -2   > TileLeft)  and
                 (Self.Left +2   < TileRight) then
                 Y := TileTop-60;
            end;
          // left
          if GetKeyState(VK_LEFT) < 0 then
            begin
              if (Self.Left+2 > TileRight) and
              (Self.Top+4< TileBottom) and
              (Self.Bottom-4>TileTop)  then
              X := TileRight;
            end;
          // right
          if GetKeyState(VK_RIGHT) < 0 then
            begin
              if (Self.Right-2 < TileLeft )and
              (Self.Top+4< TileBottom) and
              (Self.Bottom-4>TileTop)  then
              X := TileLeft -30;
            end;
        end;
    It works, but honestly... it did not do any difference, my character can still enter a wall
    from any direction if he is only like 1-2 pixel in the wall , I will try to draw this down maybe I'll understand it then why is it doing this... collision should stop it from getting in...or maybe I need to set the speed or something , I'll check vx, vy to 0 ...

    Greetings
    Robert

  4. #4

    With Trial and Error

    Hello, I've managed to do it... but still don't understand why the heck is it working

    Code:
     procedure TBlurp.DoCollision(Sprite:TSprite; var Done:boolean);
    var
       TileLeft, TileRight,
       TileTop, TileBottom: Integer;
    begin
      if Sprite is TPlatform then
        begin
          TileLeft := Trunc(TPlatform(Sprite).X);
          TileTop := Trunc(TPlatform(Sprite).Y);
          TileRight := Trunc(TPlatform(Sprite).X) + TPlatform(Sprite).Image.Width;
          TileBottom := Trunc(TPlatform(Sprite).Y) + TPlatform(Sprite).Image.Height;
    
          // up
          if (GetKeyState(VK_UP) < 0) then
            begin
              if (Self.Top+3  > TileBottom) and
                 (Self.Right  > TileLeft) and
                 (Self.Left  < TileRight) then
                  begin
                     Y := TileBottom;
                  end;
            end;
          // down
          if (GetKeyState(VK_DOWN) < 0) then
            begin
              if (Self.Bottom -3  < TileTop )  and
                 (Self.Right  > TileLeft)  and
                 (Self.Left  < TileRight) then
                  begin
                     Y := TileTop-PlayerH;
                  end;
            end;
          // left
          if GetKeyState(VK_LEFT) < 0 then
            begin
              if (Self.Left+3 > TileRight) and
              (Self.Top< TileBottom) and
              (Self.Bottom>TileTop)  then
                begin
                  X := TileRight;
                end;
            end;
          // right
          if GetKeyState(VK_RIGHT) < 0 then
            begin
              if (Self.Right-3 < TileLeft )and
              (Self.Top< TileBottom) and
              (Self.Bottom>TileTop)  then
                begin
                  X := TileLeft -PlayerW;
                end;
            end;
        end;
    end;
    it works like 99% of the time... sometimes, I can't seem to reproduce it every time I'm able to enter the tile....pfff

    EDIT : by increasing left and right to +3 and -3 it seems I'm unable to enter the wall now...evern after trying to quiet a while...

    my character is 30x60...

    Greetings
    Robert
    unSimple 0.1.zip

  5. #5
    Don't know why, but it just looks to me like the whole code is done by nearly hardest possible way. I can't even follow what the code is doing... Are you calculating collisions same place you move player? SpriteEngine way of coding is relic of the past DelphiX which was absolutely horrible way to make games. DoMove, DoCollision; all of those are optional steps that make the code more complicated than it should be. With vectors the code amount would be reduced by 60% while code would become more readable and faster.

    That said, once you've already started that way i'm not one telling you to change all the work. Just, think the code through wholly. In what order are those class methods called and how do the variables change in between. Does your collision take into account if player is 5 pixels inside the wall, aswell as 6 or 16 pixels? If your movement is not frame-based or floating point then you can't rely on certain things.

    Are the floats absolutely rounded correctly with either Trunc() or Round()? Do you draw the player from its center point or corner?

    ps. I have also msn if you'd like to chat about things more detailed. Send a pm
    Last edited by User137; 29-08-2011 at 06:56 AM.

  6. #6
    Hello,

    I'm just trying out multiple ways to do the same thing...at first this seemed unbelievable simple should it work, my original idea was this (this you will understand ... better) :

    I'm moving my character on the onMOVE with vx, vy (velocity), then testing for Collision, if my character collides with TPlatForm... then I check if he was going up down left or right... and according to that I set my character position to be positioned next to wall , like this :
    if my character is going up, and he hits the TPlatform right above I do the following , Y:=TPlatForm(Sprite).Y+TPlatForm(Sprite).Height; set my character's Y coordinate to
    TPlatForm(Sprite) -> this is the Sprite I've hit Y+ it's Height, so I'm right bellow it, no empty space... no need to check speed or whatever since it's not possible to get inside it this way.

    Anyway when I've tried this out I was amazed that it worked, and that it's such a simple solution ... the problem came when I was trying to do the following :
    Pressed up to hit the wall... then I've kept pressing up... and then tried to move left and right along the upper part... my character was moving incorrectly (at very high speed)... so I've tried the following to make it good, if I hit upper or lover sprite I've set vx:=0, if I've hit left or right I've set vy:=0, now this fixed my problem or so I thought... when I've hit the sprite above me or bellow and kept pressing up or down and at the same time left and right I was moving along the wall nicely... but if there was some obstacle next to me and I've reached it... my character "magicaly" transported itself right in top of it or bellow, since the code was always placing my character just bellow or above a sprite should it collide.
    error.jpg

    In this case I was pressing up Constantly and right as well, I was moving fine allong the wall , until I've reached the corner... there what happened was my Characters Y position was updated to the Y position of the sprite I've hit from RIGHT side.

    Code:
     unit Main;
    interface
    uses
      Windows,Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
      Dialogs, AdDraws, AdClasses, AdTypes, AdPerformanceCounter, AdDevIL,AdSprites,Math;
      const
      TileW = 32; // tile width
      TileH = 32; // tile height
      PlayerH = 60; // Player Height
      PlayerW = 30; // Player Width
      type
      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;
      end;
    var
      Form1: TForm1;
      ActTime : double;
      // camera positions
      camera_x : double;
      camera_y : double;
    
    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
              with TPlatform.Create(AdSpriteEngine) do
              begin
                Image := AdImageList.Find('WALL');
                x := ax*TileW;
                y := ay*TileH;
                z := 0;
              end;
            end;
            'X':
            begin
              with TPlatForm.Create(AdSpriteEngine) do
              begin
                Image := AdImageList.Find('WALL');
                x := ax*TileW;
                y := ay*TileH;
                z := 0;
              end;
            end;
          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;
      // setting the camera to follow our HERO
      if (X > 320) and (X < (40*TileW)-320-TileW) then
        begin
          // - 64 , because when loading the level my entire background is starting from 64 instead of 0
          // looks better if there is no black line at the edge of the level...
          camera_x:=(X*-1)+320-TileW;
        end;
      if (Y > 240) and (Y < (29*TileH)-240) then
        begin
          // 29 tiles down * 64 tile.height -240 half of the screen, only moving when at center of the screen
          // well not really the center...but close anyway :)
          camera_y:=(Y*-1)+240;
        end;
    
      if vy < 0 then
      // moving up
      begin
        // not obstructed so move normally
          y := y + vy * TimeGap
      end;
      if vy > 0 then
      // moving down
      begin
          y := y + vy * TimeGap
      end;
     if vx < 0 then
      // moving left
      begin
          x := x + vx * TimeGap
      end;
      if vx > 0 then
      // moving right
      begin
          x := x + vx * TimeGap
      end;
     
    
      Collision;
    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
          //up
          if vy < 0 then
            begin
              Y:=TPlatForm(Sprite).Y+TPlatForm(Sprite).Height;
            end;
          // down
          if vy > 0 then
            begin
              Y:=TPlatForm(Sprite).Y-Height;
            end;
          // left
          if vx < 0 then
            begin
              X:=TPlatForm(Sprite).X+TPlatForm(Sprite).Width;
            end;
          // right
          if vx > 0 then
            begin
              X:=TPlatForm(Sprite).X-Width;        
            end;
        end;
    end;
    procedure TPlatform.DoCollision(Sprite:TSprite; var Done:boolean);
    begin
    end;
     
    
    procedure TForm1.FormCreate(Sender: TObject);
    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('TallHero2');
                X := 144;
                Y := 144;
                Z := 0;
                CollisionTester := AdPixelCollisionTester;
              end;
     
        // The Entire Engine is moved left by -64 , this is because of the Level Loader ...
        Camera_X:=-TileW;
        Camera_Y:=0;
        AdSpriteEngine.X:=camera_x;
        AdSpriteEngine.Y:=camera_y;
    
        // ************* 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);
     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 > 10 then
        begin
              Blurp.vx:=0;
              Blurp.vy:=0;
              if GetKeyState(VK_LEFT) < 0 then
                begin
                  Blurp.vx:=-123;
                end;
              if GetKeyState(VK_RIGHT) < 0 then
                begin
                  Blurp.vx:=+123;
                end;
              if GetKeyState(VK_UP) < 0 then
                begin
                  Blurp.vy:=-123
                end;
              if GetKeyState(VK_DOWN) < 0 then
                begin
                  Blurp.vy:=123
                end;
    
          AdSpriteEngine.X:=camera_x;
          AdSpriteEngine.Y:=camera_y;
          ActTime := 0;
        end;
    
        AdSpriteEngine.Draw;
        AdSpriteEngine.Move(AdPerCounter.TimeGap/1000);
        AdSpriteEngine.Dead;
        // debug
     
    
        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.
    Attached the code so you can see it in work :
    test.zip


    Greetings
    Rob

    ps.: in the next post I'll try and explain what my newer improved code does...and the parts I don't understand...

  7. #7
    Code:
    procedure TBlurp.DoCollision(Sprite:TSprite; var Done:boolean);
    This procedure is acting wrong in theory. Because when you press Up and Right simultaneously you should get sprite to move both down and left edge. When you move along wall to right direction for example and hit wall, you will be in situation where player moved inside 3 different blocks. This simply will not work even with vx:=0 etc whereever.

    One note from code also, the lagtime code will not make game smooth as it is.
    Code:
        ActTime := ActTime + AdPerCounter.TimeGap;
        if ActTime > 10 then
        begin
          ...
          ActTime := 0;
        end;
    This should be:
    Code:
        ActTime := ActTime + AdPerCounter.TimeGap;
        if ActTime > 10 then
        begin
          ...
          ActTime := ActTime - 10;
        end;
    Otherwise the frametime will not try to balance it to 10 milliseconds, but instead always with little variety above 10.

    Also, this is still same topic as: http://www.pascalgamedevelopment.com...xamples)/page3
    There was many suggestions for collision detection, why did you turn them down?

  8. #8
    [I'm just experimenting... tried 3 different methods for now, this one got my atention because it's a built-in thing]


    So about the improved code (I've found this inside an example from Asphyre 2d , the SpriteEngineRPGMap one to be exact)

    Every time I move my character I write the following values into it's property...

    Attachment 579

    Code:
    Left := Round(X); 
    Top := Round(Y); 
    Right := Round(X + Image.Width);
    Bottom := Round(Y + Image.Height);
    When my character collides with the PlatForm Sprite I do this :

    1. Get same coordinates from Sprite I've just hit

    Code:
    procedure TBlurp.DoCollision(Sprite:TSprite; var Done:boolean);
    var
    TileLeft, TileRight,
    TileTop, TileBottom: Integer;
    begin
    if Sprite is TPlatform then
    begin
    TileLeft := Trunc(TPlatform(Sprite).X);
    TileTop := Trunc(TPlatform(Sprite).Y);
    TileRight := Trunc(TPlatform(Sprite).X) + TPlatform(Sprite).Image.Width;
    TileBottom := Trunc(TPlatform(Sprite).Y) + TPlatform(Sprite).Image.Height;
    // up
    if (GetKeyState(VK_UP) < 0) then
    begin
    if (Self.Top+3 > TileBottom) and
    (Self.Right > TileLeft) and
    (Self.Left < TileRight) then
    begin
    Y := TileBottom;
    end;
    end;
    Attachment 580

    Now that I drew it down... checking the logic +1 or -1 depending on direction should also be enough... maybe I'll look into my rounding... maybe I don't need it...

    And I think I also understand why it does it like this ... in this case if I'm touching a wall from left or right... but Not touching a wall from TOP... it won't place me right bellow an incorrect tile...

    I'll try to clean the code a bit more...

    Greetings
    Rob

  9. #9

    I think I got it :)

    Code:
    unit Main;
    interface
    uses
      Windows,Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
      Dialogs, AdDraws, AdClasses, AdTypes, AdPerformanceCounter, AdDevIL,AdSprites,Math;
      const
      TileW = 32; // tile width
      TileH = 32; // tile height
      PlayerH = 60; // Player Height
      PlayerW = 30; // Player Width
      type
      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
        FLeft, FTop, FRight, FBottom: double;
        nextv,v : double;
        jumping : boolean;
        protected
          procedure DoMove(TimeGap: Double);override;
          procedure DoCollision(Sprite:TSprite; var Done:boolean);override;
        public
          constructor Create(AParent:Tsprite);override;
          property Left: double read FLeft write FLeft;
          property Top: double read FTop write FTop;
          property Right: double read FRight write FRight;
          property Bottom: double read FBottom write FBottom;
        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;
      end;
    var
      Form1: TForm1;
      ActTime : double;
      // camera positions
      camera_x : double;
      camera_y : double;
    
    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
              with TPlatform.Create(AdSpriteEngine) do
              begin
                Image := AdImageList.Find('WALL');
                x := ax*TileW;
                y := ay*TileH;
                z := 0;
                CollisionTester := AdPixelCollisionTester;
              end;
            end;
            'X':
            begin
              with TPlatForm.Create(AdSpriteEngine) do
              begin
                Image := AdImageList.Find('WALL');
                x := ax*TileW;
                y := ay*TileH;
                z := 0;
                CollisionTester := AdPixelCollisionTester;
              end;
            end;
          end;
        end;
      end;
      level.Free;
    end;
    
    constructor TPlatform.Create(AParent: TSprite);
    begin
      inherited;
    end;
    constructor TBlurp.Create(AParent: TSprite);
    begin
      inherited;
      vx:=128;
      vy:=128;
      V := 10; // starting velocity, used for calculating gravity effect on our little hero
      jumping:=true; // cause our character is not jumping right now
    end;
     
    procedure TBlurp.DoMove(TimeGap: Double);
    begin
      inherited;
         Left :=   X;
         Top :=    Y;
         Right :=  X + Image.Width;
         Bottom := Y + Image.Height;
      // setting the camera to follow our HERO
      if (X > 320) and (X < (40*TileW)-320-TileW) then
        begin
          // - 64 , because when loading the level my entire background is starting from 64 instead of 0
          // looks better if there is no black line at the edge of the level...
          camera_x:=(X*-1)+320-TileW;
        end;
      if (Y > 240) and (Y < (29*TileH)-240) then
        begin
          // 29 tiles down * 64 tile.height -240 half of the screen, only moving when at center of the screen
          // well not really the center...but close anyway :)
          camera_y:=(Y*-1)+240;
        end;
      // jump
    
     { if (jumping = true) then
        begin
          Y:=Y-5;   // the kick in the but
        end;  }
      // gravity done here
     { if jumping = true then
        begin
          if nextv < 15 then nextv:=nextv+(v*TimeGap);
          Y:=Y+nextv;
        end
      else
        begin
          nextV:=0;
          jumping:=true;
        end; }
    
        if GetKeyState(VK_LEFT) < 0 then
          begin
            x := x - vx * TimeGap;
          end;
        if GetKeyState(VK_RIGHT) < 0 then
          begin
            x := x + vx * TimeGap;
          end;
        if GetKeyState(VK_UP) < 0 then
          begin
            y := y - vy * TimeGap;
          end;
        if GetKeyState(VK_DOWN) < 0 then
          begin
            y := y + vy * TimeGap;
          end;
     
      Collision;
    end;
    
    procedure TPlatform.DoMove(TimeGap : Double);
    begin
      inherited;
    end;
    
    procedure TBlurp.DoCollision(Sprite:TSprite; var Done:boolean);
    var
       TileLeft, TileRight,
       TileTop, TileBottom: double;
    begin
      if Sprite is TPlatform then
        begin
          TileLeft := TPlatform(Sprite).X;
          TileTop := TPlatform(Sprite).Y;
          TileRight := TPlatform(Sprite).X + TPlatform(Sprite).Image.Width;
          TileBottom := TPlatform(Sprite).Y + TPlatform(Sprite).Image.Height;
    
          // up
          if (GetKeyState(VK_UP) < 0) then
            begin
              if (Self.Top+1  > TileBottom) and
                 (Self.Right  > TileLeft) and
                 (Self.Left  < TileRight) then
                  begin
                     Y := TileBottom;
                  end;
            end;
          // down
          if (GetKeyState(VK_DOWN) < 0) then
            begin
              if (Self.Bottom -1  < TileTop )  and
                 (Self.Right  > TileLeft)  and
                 (Self.Left  < TileRight) then
                  begin
                     Y := TileTop-PlayerH;
                  end;
            end;
          // left
          if GetKeyState(VK_LEFT) < 0 then
            begin
              if (Self.Left+1 > TileRight) and
              (Self.Top< TileBottom) and
              (Self.Bottom>TileTop)  then
                begin
                  X := TileRight;
                end;
            end;
          // right
          if GetKeyState(VK_RIGHT) < 0 then
            begin
              if (Self.Right-1 < TileLeft )and
              (Self.Top< TileBottom) and
              (Self.Bottom>TileTop)  then
                begin
                  X := TileLeft -PlayerW;
                end;
            end;
        end;
    end;
    procedure TPlatform.DoCollision(Sprite:TSprite; var Done:boolean);
    begin
    end;
     
    
    procedure TForm1.FormCreate(Sender: TObject);
    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('TallHero2');
                X := 144;
                Y := 144;
                Z := 0;
                CollisionTester := AdPixelCollisionTester;
              end;
     
        // The Entire Engine is moved left by -64 , this is because of the Level Loader ...
        Camera_X:=-TileW;
        Camera_Y:=0;
        AdSpriteEngine.X:=camera_x;
        AdSpriteEngine.Y:=camera_y;
    
        // ************* 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);
     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 > 10 then
        begin
          AdSpriteEngine.X:=camera_x;
          AdSpriteEngine.Y:=camera_y;
          ActTime := ActTime-10;
        end;
    
        AdSpriteEngine.Draw;
        AdSpriteEngine.Move(AdPerCounter.TimeGap/1000);
        AdSpriteEngine.Dead;
        // debug
     
        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.
    Works like a charm, round trunc was my problem, and now I understand why I need +1 or -1 for x or y. Only needed to draw it down.

    Greetings
    Robert

  10. #10
    Your main problem is that you are not identifying which side you are hitting, and code you posted above has values after VK_UP all illogical.

    Maybe something like this. I'm turning the logics upside down, by assuming that DoCollision is called only when and with sprite that it collides with. So all i do is check which side the block is in relation to player and move (untested):
    Code:
    procedure TBlurp.DoCollision(Sprite:TSprite; var Done:boolean);
    var
      TileLeft, TileRight, TileTop, TileBottom: Integer;
    begin
      if Sprite is TPlatform then begin
        TileLeft := Trunc(TPlatform(Sprite).X);
        TileTop := Trunc(TPlatform(Sprite).Y);
        TileRight := TileLeft + TPlatform(Sprite).Image.Width;
        TileBottom := TileTop + TPlatform(Sprite).Image.Height;
        
        if TileLeft div TileW = trunc(X+Width/2) div TileW then 
          if TileTop div TileH < trunc(Y+Height/2) div TileH then begin
            Y := TileBottom;
          end else if TileTop div TileH > trunc(Y+Height/2) div TileH then begin
            Y := TileTop - Height;
          end;
        if TileTop div TileH = trunc(Y+Height/2) div TileH then
          if TileLeft div TileW < trunc(X+Width/2) div TileW then begin
            X := TileRight;
          end else if TileLeft div TileW > trunc(X+Width/2) div TileW then begin
            X := TileLeft - Width;
          end;
      end;
    end;
    Edited. Noticed it might have acted wrong without extra if's. And another major error hopefully fixed. Now i notice this could use more precalculation to vars... The code blew up in hands.

    Also wanted to mention this earlier. By math logics this can be simplified:
    Code:
    camera_x:=(X*-1)+320-TileW;
    Code:
    camera_x:=320-X-TileW;

    Last edit: Realising that this code of mine will not cause collision when moving towards corners, it's almost as good as nothing. So i'll stick to what i said earlier, you need something really different kind of collision method for this problem.

    The versions you showed earlier with gravity seemed to work almost fine. Just a few fixable bugs but they had overall more potential to work. One thing for example, when the jumping state is on and player hits block above, you must set the vertical velocity 0 and make player start dropping. This caused player to stick to the roof for small time.

    Last edit2 (lol i'm just bored): I think it might work with this kind of doCollision too if instead of just checking player position, use the player movement direction (vx, vy) as help to determine what wall to collide with. What mainly boggles my mind is situation where there is 3 roof blocks above, and player holds keys Left and Up. First collision test will be to the top left block which will same time cause player to stop vertical and horizontal movement in corner. That is why in above code i ignore corner blocks, but that causes another issue when there is only corner and nothing above, it wouldn't collide.
    Last edited by User137; 29-08-2011 at 11:57 AM.

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
  •