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
    [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

  2. #2

    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

  3. #3
    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
  •