Collision Course

I studied the part and examples of TRigidbody to understand the collision method.
What I do not understand is how to add an event that happens when two scenes collide.

Haven’t tried TRigidBody myself, but from the document there’re 3 events that may interest you:

  • OnCollisionEnter: Occurs when start collide with another TRigidBody
  • OnCollisionExit: Occurs when stop collide with another TRigidBody
  • OnCollisionStay: Occurs when still collide with another TRigidBody

You can for example create a OnCollisionEnter event for player, and then check TPhysicsCollisionDetails.OtherTransform to determine if the other TRigidBody belongs to NPC or something else, for example:

TNPC = class(TCastleScene);
TPlayer = class(TCastleScene);
...
procedure TPlayer.CollisionEnter(const CollisionDetails: TPhysicsCollisionDetails);
begin
  if CollisionDetails.OtherTransform <> nil then
  begin
    if CollisionDetails.OtherTransform is TNPC then
      { Do something };
  end;
end;
...
constructor TPlayer.Create(AOwner: TComponent);
var
  RBody: TRigidBody;
  Collider: TBoxCollider; 
begin
  inherited;
  RBody:= TRigidBody.Create(Self);
  RBody.Dynamic := False;
  RBody.Setup2D;
  RBody.OnCollisionEnter := @CollisionEnter;
  Collider := TBoxCollider.Create(RBody);
  Collider.Size := LocalBoundingBox.Size;
  RigidBody := RBody;
end;
...
constructor TNPC.Create(AOwner: TComponent);
var
  RBody: TRigidBody;
  Collider: TBoxCollider; 
begin
  inherited;
  RBody:= TRigidBody.Create(Self);
  RBody.Dynamic := False;
  RBody.Setup2D;
  Collider := TBoxCollider.Create(RBody);
  Collider.Size := LocalBoundingBox.Size;
  RigidBody := RBody;
end;

I did not yet analyze your full example code. I would suggest to make an even smaller example code that demonstrates only the problem you have and nothing else. Right now, we cannot run your code (it depends on data we don’t have) and it is hard to debug what is wrong in a non-trivial code we don’t understand :slight_smile: I realize that it puts more work on you, but it will also lead to faster + more useful answers.

The box collider size is in 3D, and all 3 sizes must be > 0 (not zero but larger than zero).

Loading a PNG file into TCastleScene gives you a scene that has the “depth” size = 0. Instead of doing

begin
  ...
  MyBoxCollder.Size := SomeScene.LocalBoundingBox.Size;
end;

do something like this:

var
  Size: TVector3;
begin
  ...
  Size := SomeScene.LocalBoundingBox.Size;
  Size.Z := Max(0.1, Size.Z);
  MyBoxCollder.Size := Size;
`

program Rescue_Island;

{$ifdef MSWINDOWS} {$apptype GUI} {$endif}


uses Classes, SysUtils, StrUtils, Dialogs,
  CastleWindow,CastleUtils, CastleUIControls, CastleGLImages, CastleFilesUtils,
  CastleKeysMouse, CastleVectors, CastleSoundEngine, CastleTimeUtils, CastleColors,
  CastleRectangles, CastleFonts, CastleGLUtils, Generics.Defaults, CastleUIState, CastleComponentSerialize,
  Generics.Collections,Math, CastleOnScreenMenu, CastleControls, CastleDownload,CastleSceneCore, CastleScene,
  CastleViewport, CastleCameras, CastleTransform,
  CastleImages;

type
TAvatar = class;

var
  Window: TCastleWindowbase;
  Viewport: TCastleViewport;
  Background: TCastleScene;
  Mouse: TCastleScene;
  MouseCursor: TCastleUserInterface;
  PlayerScene, NPCScene: TCastleTransform;
  Player, NPC: TAvatar;

  type
   TAvatar = class

   WalkLeft, WalkRight, WalkFront, WalkBack, StandIdle: boolean;
   WalkLeftAnim, WalkRightAnim, WalkFrontAnim, WalkBackAnim: TCastleScene;
   StandIdleAnim: TCastleScene;

   Directory: string;
   Stand: boolean;

   public
   Constructor Create;

  var
    X, Y: single;
end;


procedure SetMouse;
begin
  Mouse := TCastleScene.Create(Application);
  Viewport.Items.Add(Mouse);
  Mouse.Load('castle-data:/mouse-icons.starling-xml#fps:1, anim-naming:strict-underscore');
  Mouse.Translation := vector3(Window.MousePosition.X, Window.MousePosition.Y, 5);
end;

procedure LoadPlayer;
var
  RBody: TRigidBody;
  Collider: TBoxCollider;
  Size: TVector3;
 begin
  PlayerScene := TCastleTransform.Create(Application);
  Viewport.Items.Add(PlayerScene);

  Player := TAvatar.Create;

  Player.StandIdleAnim.Load('castle-data:/characters/john carring/casual/johnstandidle.starling-xml#fps:8,anim-naming:strict-underscore');
  Player.StandIdleAnim.Translation := Vector3(0, 0, 1);
  Player.StandIdleAnim.Scale := Vector3(0.5, 0.5, 1);

  Player.WalkLeftAnim.Load('castle-data:/characters/john carring/casual/johnwalkleft.starling-xml#fps:45,anim-naming:trailing-number');
  Player.WalkLeftAnim.Exists:= false;

  Player.WalkRightAnim.Load('castle-data:/characters/john carring/casual/johnwalkright.starling-xml#fps:45,anim-naming:trailing-number');
  Player.WalkRightAnim.Exists:= false;

  PlayerScene.Add(Player.StandIdleAnim);
  PlayerScene.Add(Player.WalkLeftAnim);
  PlayerScene.Add(Player.WalkRightAnim);

  RBody := TRigidBody.Create(PlayerScene);
  RBody.Dynamic := false;
  RBody.Animated:= true;
  RBody.Setup2D;
//  RBody.OnCollisionEnter:= @CollisionEnter);
  Collider := TBoxCollider.Create(RBody);
  Size := PlayerScene.LocalBoundingBox.Size;
  Size.Z := Max(0.1, Size.Z);
  Collider.Size := Size;

  PlayerScene.RigidBody := RBody;
  PlayerScene.Translation := Vector3(0, -200, 1);
end;

procedure LoadNPC;
var
  RBody: TRigidBody;
  Collider: TBoxCollider;
  Size: TVector3;
  begin
    NPCScene := TCastleTransform.Create(Application);
    Viewport.Items.Add(NPCScene);

    NPC := TAvatar.Create;

    NPC.StandIdleAnim.Load('castle-data:/characters/kyley carring/casual/kyleystandidle.starling-xml#fps:8,anim-naming:strict-underscore');
    NPC.StandIdleAnim.Translation := Vector3(0, 0, 1);
    NPC.StandIdleAnim.Scale := Vector3(0.5, 0.5, 1);

    NPCScene.Add(NPC.StandIdleAnim);

    RBody := TRigidBody.Create(NPCScene);
    RBody.Dynamic := false;
    RBody.Animated:= true;
    RBody.Setup2D;
  //  RBody.OnCollisionEnter:= @CollisionEnter);
    Collider := TBoxCollider.Create(RBody);
    Size := NPCScene.LocalBoundingBox.Size;
    Size.Z := Max(0.1, Size.Z);
    Collider.Size := Size;

    NPCScene.RigidBody := RBody;
    NPCScene.Translation := Vector3(200, -200, 1);
end;

constructor TAvatar.Create;
begin
  inherited;
  StandIdleAnim := TCastleScene.Create(Application);
  WalkLeftAnim := TCastleScene.Create(Application);
  WalkRightAnim := TCastleScene.Create(Application);
 end;

procedure StopWalk;
begin
  with Player do
  begin
    WalkLeft := false;
    WalkRight := false;
   Player.StandIdleAnim.Exists := true;
   Player.WalkLeftAnim.Exists := false;
   Player.WalkRightAnim.Exists := false;
  end;
end;

Procedure GoLeft;
begin
  StopWalk;
  Player.StandIdleAnim.Exists := false;
  Player.WalkLeftAnim.Exists := true;
  Player.WalkLeft := true;
  Player.Stand := false;
  Player.WalkLeftAnim.PlayAnimation('johnwalkleft-', true);
end;

Procedure StandLeft;
begin
  StopWalk;
  Player.StandIdleAnim.Exists := true;
  Player.WalkLeft := false;
  Player.Stand := true;
  Player.StandIdleAnim.PlayAnimation('johnstandleft', true);
 end;

Procedure GoRight;
begin
  StopWalk;
  Player.StandIdleAnim.Exists := false;
  Player.WalkRightAnim.Exists := true;
  Player.WalkRight := true;
  Player.Stand := false;
  Player.WalkRightAnim.PlayAnimation('johnwalkright-', true);
end;

Procedure StandRight;
begin
  StopWalk;
  Player.StandIdleAnim.Exists := true;
  Player.WalkRight := false;
  Player.Stand := true;
  Player.StandIdleAnim.PlayAnimation('johnstandright', true);
end;

Procedure StandFront;
begin
  StopWalk;
  Player.StandIdleAnim.Exists := true;
  Player.WalkFront := false;
  Player.Stand := true;
  Player.StandIdleAnim.PlayAnimation('johnstandfront', true);
end;


procedure WindowUpdate(Container: TUIContainer);
begin
   Mouse.Translation := Vector3(Viewport.PositionTo2DWorld(Window.MousePosition, true), 5);

  if Player.WalkLeft then Player.X := Player.X - 8;
  if Player.WalkRight then Player.X := Player.X + 8;

  PlayerScene.Translation := Vector3(Player.X, Player.Y, 1);

end;


 procedure WindowPress(Container: TUIContainer; const Event: TInputPressRelease);
 begin
  if Event.IsKey(keyArrowLeft) then
  begin
    if Player.Stand then GoLeft else StandLeft;
  end;

  if Event.IsKey(keyArrowRight) then
  begin
    if Player.Stand then GoRight else StandRight;
  end;

end;

 // ---------------------------------------------------------------------------

begin
  Window := TCastleWindowBase.Create(Application);
  Window.Container.UIReferenceWidth := 1024;
  Window.Container.UIReferenceHeight := 768;
  Window.Container.UIScaling := usEncloseReferenceSize;
  Window.OnUpdate := @WindowUpdate;
  Window.OnPress := @WindowPress;
  Window.Open;

  Viewport := TCastleViewport.Create(Application);
  Viewport.Setup2D;
  Viewport.FullSize := true;
  Viewport.Camera.Orthographic.Width := 1920;
  Viewport.Camera.Orthographic.Height := 1080;

  Viewport.Camera.Orthographic.Origin := Vector2(0.5, 0.5);

  Window.Controls.InsertFront(Viewport);

  Background := TCastleScene.Create(Application);
  Background.Setup2D;
  Background.Load('castle-data:/locations/westbeach1.png');
  Background.ProcessEvents := true;
  Viewport.Items.Add(Background);

MouseCursor := TCastleUserInterface.Create(Application);
MouseCursor.FullSize := true;
MouseCursor.Cursor := mcstandard;
Window.Controls.InsertFront(MouseCursor);

  SetMouse;

  LoadPlayer;
  Player.Y := -200;
  LoadNPC;


  Application.Run;
end.

Ok, I made it as small as possible now. If I make it any smaller it won’t work anymore.
I have looked at your examples on collision but don’t know how I can set this up in this code, in the Window Update procedure I guess. So collision between mouse scene and Player and NPC transforms?