Results 1 to 10 of 10

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

Hybrid View

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

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
  •