projectile physics (with ellipsoid / with linepick)

Started by RemiD, October 27, 2018, 19:13:53

Previous topic - Next topic

RemiD

projectile physics : with ellipsoid and collidables (see next posts)

removed buggy code !



projectile physics : with linepick and pickables (see next posts)

removed buggy code!


(i have noticed that sometimes (rarely), the linepick does not detect the obstacle, and the projectile goes through it... i think that this happens because the length of the linepick is too short to detect a collision, so this part can be improved...)

phodiS

Wow RemiD you are amazing!!!. I won't even pretend to understand how you did this, but it is impressive!  :o

RemiD

i have just read a few others codes, tried to understand what is going on, extracted the math formulas, simplified it my way (in words), and coded it in Blitz3d code.

the difficult thing is to understand the difference between local/global position/orientation/vector

However the version with linepick is buggy, i am working on it...

RemiD

I have found the cause of the bug, the bug is present in both codes (with ellipsoid and colidables | with linepick and pickables), but i have difficulties to find the right way (in code) to solve it.

Basically :
When an entity is translated by a vector, it has a direction and a length.
When no collision is detected, the entity is simply translated along the whole vector
When a collision is detected, a before (collision) vector is calculated, the entity is translated along this before vector, then an after (collision) vector (considering the reflection) is calculated, the entity is translated along this after vector.
but what if during the translation along the after vector, another collision happens ? well the entity will go through the obstacle...

so in a way the solution to the bug is to check for collisions not only once but before each translation... Now to put this in code is another story :D

RemiD

Finally ! i have found a right approach (procedure) to have projectile physics, using only linepick and pickables, and without having the projectile go through an obstacle when the translation vector can hit several obstacles when reflected.


;projectile physics (with linepick and pickables) 20181004

Graphics3D(1000,624,32,2)

HidePointer()

SeedRnd(MilliSecs())

Global MXDiff%
Global MYDiff%

;camera
Global Camera = CreateCamera()
CameraRange(Camera,1,1000)
CameraClsColor(Camera,001,001,001)

;origine
Origine = CreateCube()
ScaleMesh(Origine,0.03/2,0.03/2,0/03.2)
EntityColor(Origine,255,000,255)
EntityFX(Origine,1)

Global GhostRoot
Global GhostRootYaw#
Global GhostEyes
Global GhostEyesPitch#
AddGhost()

;room
Global Room = CreateCube() ;CreateCube() CreateCylinder(4) CreateCylinder(8) CreateSphere(2) CreateSphere(4) CreateSphere(8)
FlipMesh(Room)
ScaleMesh(Room,100.0/2,50.0/2,100.0/2)
PositionMesh(Room,50,25,50)
For GX% = 0 To 90 Step 10
For GZ% = 0 To 90 Step 10
  TPart = CreateCube()
  ScaleMesh(TPart,10.0/2,50.0/2,10.0/2)
  PositionMesh(TPart,10.0/2,-50.0/2,10.0/2)
  PositionMesh(TPart,GX,Rand(1,12),GZ)
  AddMesh(TPart,Room) : FreeEntity(TPart)
Next
Next
PositionEntity(Room,0,0,0,True)
EntityColor(Room,125,125,125)

;projectiles
Global ProjectilesCount%
Type Projectile
Field State%
Field Shape
Field Radius#
Field Gravity#
Field VX#, VY#, VZ#
Field VLength#
End Type

Const CIdle% = 1
Const CTranslate% = 2

Global Pivot = CreatePivot()

EntityPickMode(Room,2)

DLight = CreateLight(1)
LightColor(DLight,255,255,255)
PositionEntity(DLight,50,1000,-1000)
RotateEntity(DLight,45,0,0)

AmbientLight(064,064,064)

PositionEntity(GhostRoot,50,25+1.65,90,True)
GhostRootYaw = 180

Global MainLoopTimer = CreateTimer(30)

;mainloop
Main()

End()

Function Main()

Repeat

  MainLoopMilliStart% = MilliSecs()

  MXDiff = MouseXSpeed()
  MYDiff = MouseYSpeed()

  UpdateGhost()

  ;update projectiles
  UpdateProjectiles()

  PositionRotateEntityLikeOtherEntity(Camera,GhostEyes)

  ;wireframe mode off/on
  WireFrame(False)
  If( KeyDown(2)=True )
   WireFrame(True)
  EndIf

  ;render
  SetBuffer(BackBuffer())
  RenderWorld()
 
  Color(255,255,255)
  Text(0,0,"ProjectilesCount = "+ProjectilesCount)
  PhysicsCount% = 0
  For pr.Projectile = Each Projectile
   If( pr\State = CTranslate )
    PhysicsCount = PhysicsCount + 1
   EndIf
  Next
  Text(0,15,"Physics active on "+PhysicsCount+" projectiles")

  Text(500,0,"Tris = "+TrisRendered())
  Text(500,16,"FPS = "+FPS)

  ;display the result on the screen
  ;Flip(1)
  WaitTimer(MainLoopTimer)
  VWait():Flip(False)

  MainLoopMilliTime = MilliSecs() - MainLoopMilliStart
  If( MainLoopMilliTime < 1 )
   MainLoopMilliTime = 1
  EndIf

  FPS% = 1000.0/MainLoopMilliTime

Until( KeyDown(1)=1 )

End Function

Function PositionEntityLikeOtherEntity(Entity,OEntity)

PositionEntity(Entity,EntityX(OEntity,True),EntityY(OEntity,True),EntityZ(OEntity,True),True)

End Function

Function RotateEntityLikeOtherEntity(Entity,OEntity)

RotateEntity(Entity,EntityPitch(OEntity,True),EntityYaw(OEntity,True),EntityRoll(OEntity,True),True)

End Function

Function PositionRotateEntityLikeOtherEntity(Entity,OEntity)

PositionEntity(Entity,EntityX(OEntity,True),EntityY(OEntity,True),EntityZ(OEntity,True),True)
RotateEntity(Entity,EntityPitch(OEntity,True),EntityYaw(OEntity,True),EntityRoll(OEntity,True),True)

End Function

Function Distance2D#(PAX#,PAZ#,PBX#,PBZ#)

Distance2D# = Sqr( ( ( PBX - PAX ) * ( PBX - PAX ) ) + ( ( PBZ - PAZ ) * ( PBZ - PAZ ) ) )
Return Distance2D

End Function

Function Distance3D#(PAX#,PAY#,PAZ#,PBX#,PBY#,PBZ#)

Distance3D# = Sqr( ( ( PBX - PAX ) * ( PBX - PAX ) ) + ( ( PBY - PAY ) * ( PBY - PAY ) ) + ( ( PBZ - PAZ ) * ( PBZ - PAZ ) ) )
Return Distance3D

End Function

Function Angle3D#(V1X#,V1Y#,V1Z#,V2X#,V2Y#,V2Z#)

D# = (V1X*V2X) + (V1Y*V2Y) + (V1Z*V2Z)
M# = Sqr( V1X*V1X + V1Y*V1Y + V1Z*V1Z ) * Sqr( V2X*V2X + V2Y*V2Y + V2Z*V2Z )
A# = ACos(D#/M#)
Return A#

End Function

Function AddGhost()

GhostRoot = CreatePivot()

GhostEyes = CreatePivot()
PositionRotateEntityLikeOtherEntity(GhostEyes,GhostRoot)
EntityParent(GhostEyes,GhostRoot,True)

End Function

Function UpdateGhost()

MoveMouse(GraphicsWidth()/2,GraphicsHeight()/2)
GhostEyesPitch = GhostEyesPitch + Float(MYDiff)/10
If( GhostEyesPitch < -89 )
  GhostEyesPitch = -89
Else If( GhostEyesPitch > 89 )
  GhostEyesPitch = 89
EndIf
RotateEntity(GhostEyes,GhostEyesPitch,0,0,False)
GhostRootYaw = GhostRootYaw - Float(MXDiff)/10
RotateEntity(GhostRoot,0,GhostRootYaw,0,False)

If( KeyDown(42) = 0 And KeyDown(29) = 0 )
  Speed# = 0.1
Else If( KeyDown(42) = 1 And KeyDown(29) = 0 )
  Speed# = 1
Else If( KeyDown(42) = 0 And KeyDown(29) = 1 )
  Speed# = 0.01
EndIf

If( KeyDown(17)=1 )
  MoveEntity(GhostRoot,0,0,Speed)
Else If( KeyDown(31)=1 )
  MoveEntity(GhostRoot,0,0,-Speed)
EndIf
If( KeyDown(30)=1 )
  MoveEntity(GhostRoot,-Speed,0,0)
Else If( KeyDown(32)=1 )
  MoveEntity(GhostRoot,Speed,0,0)
EndIf
If( KeyDown(16)=1 )
  MoveEntity(GhostRoot,0,-Speed,0)
Else If( KeyDown(18)=1 )
  MoveEntity(GhostRoot,0,Speed,0)
EndIf

If( MouseHit(1)=1 )
  DebugLog("")
  ;create a new projectile
  ProjectilesCount = ProjectilesCount + 1
  pr.Projectile = New Projectile
 
  pr\Radius# = Rnd(0.05,0.5)/2*10
  pr\Shape = CreateSphere(16)
  ScaleMesh(pr\Shape,pr\Radius,pr\Radius,pr\Radius)
  ThrowForce# = 46.940*2.0/30.0
  pr\Gravity = 0

  ;calculate translation vector (whole vector)
  TFormVector(0,0,+ThrowForce,GhostEyes,0)
  pr\VX = TFormedX() : pr\VY = TFormedY() : pr\VZ = TFormedZ()

  ;calculate translation length (whole length)
  pr\VLength = Sqr( pr\VX*pr\VX + pr\VY*pr\VY + pr\VZ*pr\VZ )
  PositionEntity(pr\Shape,EntityX(GhostEyes,True),EntityY(GhostEyes,True),EntityZ(GhostEyes,True),True)

  ;state = translate
  pr\State = CTranslate
  EntityColor(pr\Shape,000,000,255)

  EntityRadius(pr\Shape,pr\Radius-0.01)
  EntityPickMode(pr\Shape,1)

EndIf

End Function

Function UpdateProjectiles()

For pr.Projectile = Each Projectile

  If( pr\State = CIdle )

   ;

  Else If( pr\State = CTranslate )

   ;calculate new Whole vector

   ;increase gravity force
   pr\Gravity = 9.81/30.0/2

   ;add gravity force to Whole vector
   pr\VX = pr\VX : pr\VY = pr\VY-pr\Gravity : pr\VZ = pr\VZ

   ;(add air friction to Whole vector)
   ;pr\VX = pr\VX*0.999 : pr\VY = pr\VY*0.999 : pr\VZ = pr\VZ*0.999

   ;calculate new Whole length
   pr\VLength = Sqr( pr\VX*pr\VX + pr\VY*pr\VY + pr\VZ*pr\VZ )

   ;initialize Remaining vector
   RVX# = pr\VX : RVY# = pr\VY : RVZ# = pr\VZ

   ;initialize Remaining length
   RLength# = pr\VLength

   Repeat

    ;store Before position
    befX# = EntityX(pr\Shape,True) : befY# = EntityY(pr\Shape,True) : befZ# = EntityZ(pr\Shape,True)

    ;check if there is an obstacle in this direction at the Remaining length (+radius)
    picEnt% = LinePick(befX,befY,befZ,RVX,RVY,RVZ,pr\Radius)

    ;if yes
    If( picEnt <> 0 )

     ;store pic position
     picX# = PickedX() : picY# = PickedY() : picZ# = PickedZ()
     ;store pic normal
     picNX# = PickedNX() : picNY# = PickedNY() : picNZ# = PickedNZ()

     ;calculate When position
     PositionEntity(Pivot,picX,picY,picZ,True) : AlignToVector(Pivot,picNX,picNY,picNZ,3,1.0) : MoveEntity(Pivot,0,0,pr\Radius)
     wheX# = EntityX(Pivot,True) : wheY# = EntityY(Pivot,True) : wheZ# = EntityZ(Pivot,True)

     ;calculate Before length (Before position -> When position)
     BLength# = Distance3D(befX,befY,befZ,wheX,wheY,wheZ)
     ;calculate Before coef (BLength/RLength)
     BCoef# = BLength/RLength
     ;calculate Before vector
     BVX# = RVX*BCoef : BVY# = RVY*BCoef : BVZ# = RVZ*BCoef

     ;translate entity with Before vector
     TranslateEntity(pr\Shape,BVX,BVY,BVZ,True)

     ;calculate New vector (reflection vector)
     DP# = RVX*picNX + RVY*picNY + RVZ*picNZ
     NFX# = -2.0*picNX*DP : NFY# = -2.0*picNY*DP : NFZ# = -2.0*picNZ*DP
     NVX# = RVX+NFX*1.1 : NVY# = RVY+NFY*1.1 : NVZ# = RVZ+NFZ*1.1

     ;calculate angle between Remaining vector and New vector
     A# = Angle3D(RVX,RVY,RVZ,NVX,NVY,NVZ)

     ;attenuate New vector depending on angle
     AttCoef# = (1.0-(A/180.0))
     
     ;DebugLog(A+" "+AttCoef)
     NVX = NVX*AttCoef : NVY = NVY*AttCoef : NVZ = NVZ*AttCoef

     ;attenuate New vector depending on arbitrary coef
     ;NVX = NVX*0.9 : NVY = NVY*0.9 : NVZ = NVZ*0.9

     ;calculate New length
     NLength# = Sqr( NVX*NVX + NVY*NVY + NVZ*NVZ )

     ;initialize Remaining vector
     RVX = NVX : RVY = NVY : RVZ = NVZ
     ;initialize Remaining length
     RLength = NLength

     ;
     pr\VX = NVX : pr\VY = NVY : pr\VZ = NVZ
     pr\VLength = NLength

     ;check if Remaining length is inferior or equal to gravityforce
     ;if yes
     If( RLength < pr\Gravity )
   
      EntityColor(pr\Shape,125,255,255)

      ;check if the ground below is near or far
      picEnt% = LinePick(wheX,wheY,wheZ,0,-100,0,pr\Radius)
      If( picEnt <> 0 )
       picX# = PickedX() : picY# = PickedY() : picZ# = PickedZ()     
       ;if near
       If( wheY-picY < pr\Radius+pr\Gravity )
     
        ;0 Remaining vector
        RVX = 0 : RVY = 0 : RVZ = 0
        ;0 Remaining length
        RLength = 0
        ;0 gravity force
        pr\Gravity = 0

        ;state = idle
        pr\State = CIdle
        EntityColor(pr\Shape,125,125,255)

       EndIf
       ;if far
        ;

      EndIf

     EndIf
     
    Else If( picEnt = 0 )
   
     ;translate entity with Remaining vector
     TranslateEntity(pr\Shape,RVX,RVY,RVZ,True)

     ;0 Remaining vector
     RVX = 0 : RVY = 0 : RVZ = 0
     ;0 Remaining length
     RLength = 0

    EndIf

   Until( Rlength = 0 )

  EndIf

  If( pr <> Null )
   If( EntityY(pr\Shape,True) < -100 )
    ProjectilesCount = ProjectilesCount - 1
    FreeEntity(pr\Shape) : Delete(pr)
   EndIf
  EndIf

Next

End Function

8)


;projectile physics (with ellipsoid and collidables) 20181004

Graphics3D(1000,624,32,2)

HidePointer()

SeedRnd(MilliSecs())

Global MXDiff%
Global MYDiff%

;camera
Global Camera = CreateCamera()
CameraRange(Camera,1,1000)
CameraClsColor(Camera,001,001,001)

;origine
Origine = CreateCube()
ScaleMesh(Origine,0.03/2,0.03/2,0/03.2)
EntityColor(Origine,255,000,255)
EntityFX(Origine,1)

Global GhostRoot
Global GhostRootYaw#
Global GhostEyes
Global GhostEyesPitch#
AddGhost()

;room
Global Room = CreateCube() ;CreateCube() CreateCylinder(4) CreateCylinder(8) CreateSphere(2) CreateSphere(4) CreateSphere(8)
FlipMesh(Room)
ScaleMesh(Room,100.0/2,50.0/2,100.0/2)
PositionMesh(Room,50,25,50)
For GX% = 0 To 90 Step 10
For GZ% = 0 To 90 Step 10
  TPart = CreateCube()
  ScaleMesh(TPart,10.0/2,50.0/2,10.0/2)
  PositionMesh(TPart,10.0/2,-50.0/2,10.0/2)
  PositionMesh(TPart,GX,Rand(1,12),GZ)
  AddMesh(TPart,Room) : FreeEntity(TPart)
Next
Next
PositionEntity(Room,0,0,0,True)
EntityColor(Room,125,125,125)

;projectiles
Global ProjectilesCount%
Type Projectile
Field State%
Field Shape
Field Radius#
Field Gravity#
Field VX#, VY#, VZ#
Field VLength#
Field befX#, befY#, befZ#
End Type

Const CIdle% = 1
Const CTranslate% = 2

Global Pivot = CreatePivot()

Const GRoom% = 1
Const GProjectile% = 2
EntityType(Room,GRoom)
Collisions(GProjectile,GRoom,2,1)
Collisions(GProjectile,GProjectile,1,1)

EntityPickMode(Room,2)

DLight = CreateLight(1)
LightColor(DLight,255,255,255)
PositionEntity(DLight,50,1000,-1000)
RotateEntity(DLight,45,0,0)

AmbientLight(064,064,064)

PositionEntity(GhostRoot,50,25+1.65,90,True)
GhostRootYaw = 180

Global MainLoopTimer = CreateTimer(30)

;mainloop
Main()

End()

Function Main()

Repeat

  MainLoopMilliStart% = MilliSecs()

  MXDiff = MouseXSpeed()
  MYDiff = MouseYSpeed()

  UpdateGhost()

  ;update projectiles
  UpdateProjectilesBefore()

  UpdateWorld()

  UpdateProjectilesAfter()

  PositionRotateEntityLikeOtherEntity(Camera,GhostEyes)

  ;wireframe mode off/on
  WireFrame(False)
  If( KeyDown(2)=True )
   WireFrame(True)
  EndIf

  ;render
  SetBuffer(BackBuffer())
  RenderWorld()
 
  Color(255,255,255)
  Text(0,0,"ProjectilesCount = "+ProjectilesCount)
  PhysicsCount% = 0
  For pr.Projectile = Each Projectile
   If( pr\State = CTranslate )
    PhysicsCount = PhysicsCount + 1
   EndIf
  Next
  Text(0,15,"Physics active on "+PhysicsCount+" projectiles")

  Text(500,0,"Tris = "+TrisRendered())
  Text(500,16,"FPS = "+FPS)

  ;display the result on the screen
  ;Flip(1)
  WaitTimer(MainLoopTimer)
  VWait():Flip(False)

  MainLoopMilliTime = MilliSecs() - MainLoopMilliStart
  If( MainLoopMilliTime < 1 )
   MainLoopMilliTime = 1
  EndIf

  FPS% = 1000.0/MainLoopMilliTime

Until( KeyDown(1)=1 )

End Function

Function PositionEntityLikeOtherEntity(Entity,OEntity)

PositionEntity(Entity,EntityX(OEntity,True),EntityY(OEntity,True),EntityZ(OEntity,True),True)

End Function

Function RotateEntityLikeOtherEntity(Entity,OEntity)

RotateEntity(Entity,EntityPitch(OEntity,True),EntityYaw(OEntity,True),EntityRoll(OEntity,True),True)

End Function

Function PositionRotateEntityLikeOtherEntity(Entity,OEntity)

PositionEntity(Entity,EntityX(OEntity,True),EntityY(OEntity,True),EntityZ(OEntity,True),True)
RotateEntity(Entity,EntityPitch(OEntity,True),EntityYaw(OEntity,True),EntityRoll(OEntity,True),True)

End Function

Function Distance2D#(PAX#,PAZ#,PBX#,PBZ#)

Distance2D# = Sqr( ( ( PBX - PAX ) * ( PBX - PAX ) ) + ( ( PBZ - PAZ ) * ( PBZ - PAZ ) ) )
Return Distance2D

End Function

Function Distance3D#(PAX#,PAY#,PAZ#,PBX#,PBY#,PBZ#)

Distance3D# = Sqr( ( ( PBX - PAX ) * ( PBX - PAX ) ) + ( ( PBY - PAY ) * ( PBY - PAY ) ) + ( ( PBZ - PAZ ) * ( PBZ - PAZ ) ) )
Return Distance3D

End Function

Function Angle3D#(V1X#,V1Y#,V1Z#,V2X#,V2Y#,V2Z#)

D# = (V1X*V2X) + (V1Y*V2Y) + (V1Z*V2Z)
M# = Sqr( V1X*V1X + V1Y*V1Y + V1Z*V1Z ) * Sqr( V2X*V2X + V2Y*V2Y + V2Z*V2Z )
A# = ACos(D#/M#)
Return A#

End Function

Function AddGhost()

GhostRoot = CreatePivot()

GhostEyes = CreatePivot()
PositionRotateEntityLikeOtherEntity(GhostEyes,GhostRoot)
EntityParent(GhostEyes,GhostRoot,True)

End Function

Function UpdateGhost()

MoveMouse(GraphicsWidth()/2,GraphicsHeight()/2)
GhostEyesPitch = GhostEyesPitch + Float(MYDiff)/10
If( GhostEyesPitch < -89 )
  GhostEyesPitch = -89
Else If( GhostEyesPitch > 89 )
  GhostEyesPitch = 89
EndIf
RotateEntity(GhostEyes,GhostEyesPitch,0,0,False)
GhostRootYaw = GhostRootYaw - Float(MXDiff)/10
RotateEntity(GhostRoot,0,GhostRootYaw,0,False)

If( KeyDown(42) = 0 And KeyDown(29) = 0 )
  Speed# = 0.1
Else If( KeyDown(42) = 1 And KeyDown(29) = 0 )
  Speed# = 1
Else If( KeyDown(42) = 0 And KeyDown(29) = 1 )
  Speed# = 0.01
EndIf

If( KeyDown(17)=1 )
  MoveEntity(GhostRoot,0,0,Speed)
Else If( KeyDown(31)=1 )
  MoveEntity(GhostRoot,0,0,-Speed)
EndIf
If( KeyDown(30)=1 )
  MoveEntity(GhostRoot,-Speed,0,0)
Else If( KeyDown(32)=1 )
  MoveEntity(GhostRoot,Speed,0,0)
EndIf
If( KeyDown(16)=1 )
  MoveEntity(GhostRoot,0,-Speed,0)
Else If( KeyDown(18)=1 )
  MoveEntity(GhostRoot,0,Speed,0)
EndIf

If( MouseHit(1)=1 )
  DebugLog("")
  ;create a new projectile
  ProjectilesCount = ProjectilesCount + 1
  pr.Projectile = New Projectile
 
  pr\Radius# = Rnd(0.05,0.5)/2*10
  pr\Shape = CreateSphere(16)
  ScaleMesh(pr\Shape,pr\Radius,pr\Radius,pr\Radius)
  ThrowForce# = 46.940*2.0/30.0
  pr\Gravity = 0

  ;calculate translation vector (whole vector)
  TFormVector(0,0,+ThrowForce,GhostEyes,0)
  pr\VX = TFormedX() : pr\VY = TFormedY() : pr\VZ = TFormedZ()

  ;calculate translation length (whole length)
  pr\VLength = Sqr( pr\VX*pr\VX + pr\VY*pr\VY + pr\VZ*pr\VZ )
  PositionEntity(pr\Shape,EntityX(GhostEyes,True),EntityY(GhostEyes,True),EntityZ(GhostEyes,True),True)

  ;state = translate
  pr\State = CTranslate
  EntityColor(pr\Shape,000,000,255)

  EntityRadius(pr\Shape,pr\Radius-0.01)
  EntityType(pr\Shape,GProjectile)
  EntityPickMode(pr\Shape,2)

EndIf

End Function

Function UpdateProjectilesBefore()

For pr.Projectile = Each Projectile

  If( pr\State = CIdle )

   ;

  Else If( pr\State = CTranslate )

   ;calculate new Whole vector

   ;increase gravity force
   pr\Gravity = 9.81/30.0/2

   ;add gravity force to Whole vector
   pr\VX = pr\VX : pr\VY = pr\VY-pr\Gravity : pr\VZ = pr\VZ

   ;(add air friction to Whole vector)
   ;pr\VX = pr\VX*0.999 : pr\VY = pr\VY*0.999 : pr\VZ = pr\VZ*0.999

   ;calculate new Whole length
   pr\VLength = Sqr( pr\VX*pr\VX + pr\VY*pr\VY + pr\VZ*pr\VZ )

   ;store Before position
   pr\befX# = EntityX(pr\Shape,True) : pr\befY# = EntityY(pr\Shape,True) : pr\befZ# = EntityZ(pr\Shape,True)

   ;translate entity with whole vector
   TranslateEntity(pr\Shape,pr\VX,pr\VY,pr\VZ,True)

  EndIf

  If( pr <> Null )
   If( EntityY(pr\Shape,True) < -100 )
    ProjectilesCount = ProjectilesCount - 1
    FreeEntity(pr\Shape) : Delete(pr)
   EndIf
  EndIf

Next

End Function

Function UpdateProjectilesAfter()

For pr.Projectile = Each Projectile

  If( pr\State = CIdle )

   ;

  Else If( pr\State = CTranslate )

   ;check if a collision happened when translating entity along whole vector
   nc% = CountCollisions(pr\Shape)
   ;if yes
   If( nc > 0 )

    ;collision detected, get infos about the collision
    colEnt% = CollisionEntity(pr\Shape,1)
    ;store collision position
    colX# = CollisionX(pr\Shape,1) : colY# = CollisionY(pr\Shape,1) : colZ# = CollisionZ(pr\Shape,1)
    ;store collision normal
    colNX# = CollisionNX(pr\Shape,1) : colNY# = CollisionNY(pr\Shape,1) : colNZ# = CollisionNZ(pr\Shape,1)

    ;calculate When position
    wheX# = EntityX(pr\Shape,True) : wheY# = EntityY(pr\Shape,True) : wheZ# = EntityZ(pr\Shape,True)

    ;calculate New vector (reflection vector)
    DP# = pr\VX*colNX + pr\VY*colNY + pr\VZ*colNZ
    NFX# = -2.0*colNX*DP : NFY# = -2.0*colNY*DP : NFZ# = -2.0*colNZ*DP
    NVX# = pr\VX+NFX*1.1 : NVY# = pr\VY+NFY*1.1 : NVZ# = pr\VZ+NFZ*1.1

    ;calculate angle between Remaining vector and New vector
    A# = Angle3D(pr\VX,pr\VY,pr\VZ,NVX,NVY,NVZ)

    ;attenuate New vector depending on angle
    AttCoef# = (1.0-(A/180.0))
     
    ;DebugLog(A+" "+AttCoef)
    NVX = NVX*AttCoef : NVY = NVY*AttCoef : NVZ = NVZ*AttCoef

    ;calculate New length
    NLength# = Sqr( NVX*NVX + NVY*NVY + NVZ*NVZ )

    pr\VX = NVX : pr\VY = NVY : pr\VZ = NVZ
    pr\VLength = NLength

    ;check if Remaining length is inferior or equal to gravityforce
    ;if yes
    If( pr\VLength < pr\Gravity )
     EntityColor(pr\Shape,125,255,255)

     ;check if the ground below is near or far
     picEnt% = LinePick(wheX,wheY,wheZ,0,-100,0,pr\Radius-0.01)
     If( picEnt <> 0 )
      picX# = PickedX() : picY# = PickedY() : picZ# = PickedZ()     
      ;if near
      If( wheY-picY < pr\Radius+pr\Gravity )
     
       ;0 Remaining vector
       pr\VX = 0 :  pr\VY = 0 : pr\VZ = 0
       ;0 Remaining length
       pr\VLength = 0
       ;0 gravity force
       pr\Gravity = 0

       ;state = idle
       pr\State = CIdle
       EntityColor(pr\Shape,125,125,255)

      EndIf
      ;if far
       ;

     EndIf

    EndIf
     
   Else If( collEnt = 0 )
   
    ;

   EndIf

  EndIf

Next

End Function

;)