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
    The collision depends very much on how the player is allowed to move. For example if you would let player to move freely in 360 degrees you'd have a very different collision algorithm, than if player can only move vertically or horizontally.

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

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

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

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

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

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

    { if X <= 0 then X:=0;
    if X > 640-Image.Width then X:=640-Image.Width;
    if Y < 0 then Y:=0;
    if Y > 480-Image.Height then Y:=480-Image.Height; }
    Collision;
    end;
    And why are you moving 4 pixels each time? Doesn't this mean you are not going to get frame-independent movement in your game?
    I would have thought you should move by your velocity (4 pixels?) times by the frame time (TimeGap)?

    cheers,
    Paul

  3. #3
    Hello,

    Erm...I think I'm doing the movement incorrectly... I've cleaned up my code a bit, I'm doing the movement in the IDLE loop of my Form... every
    Code:
         ActTime := ActTime + AdPerCounter.TimeGap;
        if ActTime > 25 then
    I've limited the max framerate to 60, and by pressing any of the direction keys I move the Player.X or Y position with +4 or -4 to any direction...

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

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

    Complete code :

    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
        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);
    begin
      inherited;
      Collision;
    end;
    function TForm1.WallAt(x,y: Integer): Boolean;
    // tests for the presence of a wall at a tile location
    begin
      Result := True;
      Result := TileInfo[x,y].IsWall;
    end;
    function  TForm1.GetCorners(aSpr: TSprite): TCorners;
    // get object corners, includes velocity!
    begin
      // calculate tile index for each object corner
      Result.Left      := Math.Floor((aSpr.x -4) / 64);
      Result.Right     := Math.Floor((aSpr.x + aSpr.Width) / 64);
      Result.Up        := Math.Floor((aSpr.y -4) / 64);
      Result.Down      := Math.Floor((aSpr.y + aSpr.Height) / 64);
      // if true then there is a wall there
      Result.WallUL    := WallAt(Result.Left  ,Result.Up);
      Result.WallUR    := WallAt(Result.Right ,Result.Up);
      Result.WallDL    := WallAt(Result.Left  ,Result.Down);
      Result.WallDR    := WallAt(Result.Right ,Result.Down);
    end;
     
    
    procedure TPlatform.DoMove(TimeGap : Double);
    begin
      inherited;
      Collision;
    end;
    
    procedure TBlurp.DoCollision(Sprite:TSprite; var Done:boolean);
    begin
    end;
    procedure TPlatform.DoCollision(Sprite:TSprite; var Done:boolean);
    begin
    end;
     
    
    procedure TForm1.FormCreate(Sender: TObject);
    var i: integer;
    begin
      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 > 25 then
        begin
             cnrs := GetCorners(Blurp);
              if GetKeyState(VK_LEFT) < 0 then
                begin
                  if not cnrs.WallUL and not cnrs.WallDL then
                    begin
                      Blurp.X:=Blurp.X-4;
                      AdSpriteEngine.X:=AdSpriteEngine.X+4;
                    end;
                end;
              if GetKeyState(VK_RIGHT) < 0 then
                begin
                  if not cnrs.WallUR and not cnrs.WallDR then
                    begin
                      Blurp.X:=Blurp.X+4;
                      AdSpriteEngine.X:=AdSpriteEngine.X-4;
                    end;
                end;
              if GetKeyState(VK_UP) < 0 then
                begin
                  if not cnrs.WallUL and not cnrs.WallUR then
                    begin
                      Blurp.y := Blurp.y - 4;
                      AdSpriteEngine.Y:=AdSpriteEngine.Y+4;
                    end;
                end;
              if GetKeyState(VK_DOWN) < 0 then
                begin
                  if not cnrs.WallDL and not cnrs.WallDR then
                    begin
                      Blurp.Y:=Blurp.Y+4;
                      AdSpriteEngine.Y:=AdSpriteEngine.Y-4;
                    end;
                end;
    
          ActTime := 0;
        end;
    
        AdSpriteEngine.Draw;
        AdSpriteEngine.Move(25/1000);
        AdSpriteEngine.Dead;
        // debug
        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.

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

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

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

    cheers,
    Paul

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

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
  •