simple collision system using linepicks and pickables

Started by RemiD, October 14, 2018, 20:21:23

Previous topic - Next topic

RemiD

Hello,

I have already posted a more complex version of a collision system using linepicks and pickable shapes, but this one is simple, for simple 3d games with turning moving characters / bullets, for those who want to understand the approach

Graphics3D(1000,625,32,2)

HidePointer()

SeedRnd(MilliSecs())

;----------premade shapes----------
Global XRock = CreateRock(8,0.4)
ScaleMesh(XRock,2.0/2,1.5/2,2.0/2)
PositionMesh(XRock,0,1.5/2-0.5,0)
EntityColor(XRock,125,125,125)

Global XTree = CreateTree()
HideEntity(XTree)

Global XPlayer = CreateCylinder(16)
ScaleMesh(XPlayer,0.5/2,1.75/2,0.5/2)
PositionMesh(XPlayer,0,0.875,0)
EntityColor(XPlayer,000,000,255)
HideEntity(XPlayer)

Global XBot = CreateCylinder(16)
ScaleMesh(XBot,0.5/2,1.75/2,0.5/2)
PositionMesh(XBot,0,0.875,0)
EntityColor(XBot,125,125,255)
HideEntity(XBot)

Global XBullet = CreateSphere(8)
ScaleMesh(XBullet,0.2/2,0.2/2,0.2/2)
EntityColor(XBullet,125,125,125)
HideEntity(XBullet)

Global XParticle = CreateCube()
ScaleMesh(XParticle,0.04/2,0.04/2,0.04/2)
EntityColor(XParticle,125,125,125)
HideEntity(XParticle)

;-----------------------------------

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

;input
Global MXDiff%
Global MYDiff%

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

;ground
Global GroundShape
AddGround()

;rocks
Type Rock
Field Shape
End Type
AddRocks()

;trees
Type Tree
Field Shape
End Type
AddTrees()

;bots
Type Bot
Field Shape
Field Life#
Field VX#
Field VY#
Field VZ#
Field PrevMS%
End Type
AddBots()

;player
Global PlayerShape
Global PlayerYaw#
Global PlayerLife#
Global PlayerVX#
Global PlayerVY#
Global PlayerVZ#
Global PlayerPrevMS%
AddPlayer()

;bullets
Type Bullet
Field Shape
Field Life#
Field VX#
Field VY#
Field VZ#
End Type

;particles
Type Particle
Field Shape
Field Life#
End Type

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

AmbientLight(064,064,064)

;setup pickables entities
EntityPickMode(GroundShape,2)
For r.Rock = Each Rock
EntityPickMode(r\Shape,2)
Next
For t.Tree = Each tree
EntityPickMode(t\Shape,2)
Next
For b.Bot = Each Bot
EntityPickMode(b\Shape,2)
Next
EntityPickMode(PlayerShape,2)

Global PickPoint = CreateCube()
ScaleMesh(PickPoint,0.03/2,0.03/2,0.03/2)
EntityColor(PickPoint,255,000,255)
EntityFX(PickPoint,1)

Global PickNormal = CreateCube()
ScaleMesh(PickNormal,0.01/2,0.01/2,0.3/2)
PositionMesh(PickNormal,0,0,0.3/2)
EntityColor(PickNormal,125,000,125)
EntityFX(PickNormal,1)

PositionEntity(Camera,EntityX(PlayerShape,True),EntityY(PlayerShape,True),EntityZ(PlayerShape,True))
MoveEntity(Camera,0,1.65+1.5,-3.0)
TurnEntity(Camera,22.5,0,0)
EntityParent(Camera,PlayerShape)

Global BulletsCount% = 0 ;to track how many bullets are active in the scene
Global ParticlesCount% = 0 ;to track how many particles are active in the scene

Global MainLoopTimer = CreateTimer(30) ;to lock the FPS at 30FPS max

;mainloop
Main()

End()

Function Main()

Repeat

  MXDiff = MouseXSpeed()
  MYDiff = MouseYSpeed()

  ;update player depending on input (mouse and keyboard)
  UpdatePlayer()

  ;update bots depending on ai
  UpdateBots()

  ;update bullets
  UpdateBullets()

  ;update particles
  UpdateParticles()

  WireFrame(False)
  If( KeyDown(2)=True )
   WireFrame(True)
  EndIf

  ;render
  RenderWorld()

  Text(0,0,"BulletsCount = "+BulletsCount)
  Text(0,16,"ParticlesCount = "+ParticlesCount)

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

Until( KeyDown(1)=1 )

End Function

Function CreateRock(Details%=8,MaxIrregularity#=0.3)

SMesh = CreateSphere(Details)
SSurface = GetSurface(SMesh,1)

DMesh = CreateMesh()
DSurface = CreateSurface(DMesh)
DVerticesCount% = 0

;for each triangle of the source surface
For STI% = 0 To CountTriangles(SSurface)-1 Step 1

  ;for vertex0 of this triangle
  SV0I% = TriangleVertex(SSurface,STI,0)
  ;get this vertex0 position
  X# = VertexX(SSurface,SV0I) : Y# = VertexY(SSurface,SV0I) : Z# = VertexZ(SSurface,SV0I)
  ;check if there is already a vertex existing at this position in the destination surface (with a tolerance of 0.01unit)
  TVI% = -1
  For DVI% = 0 To CountVertices(DSurface)-1 Step 1
   If( VertexX(DSurface,DVI) >= X-0.01 And VertexX(DSurface,DVI) <= X+0.01 And VertexY(DSurface,DVI) >= Y-0.01 And VertexY(DSurface,DVI) <= Y+0.01 And VertexZ(DSurface,DVI) >= Z-0.01 And VertexZ(DSurface,DVI) <= Z+0.01 )
    TVI = DVI
    Exit
   EndIf
  Next
  ;if yes
  If( TVI <> - 1 )
   ;use this vertex
   DV0I% = TVI
  ;if no
  Else If( TVI% = - 1 )
   ;create a new vertex in the destination surface
   DVerticesCount = DVerticesCount + 1
   DV0I% = DVerticesCount - 1
   AddVertex(DSurface,X,Y,Z)
  EndIf

  ;for vertex1 of this triangle
  SV1I% = TriangleVertex(SSurface,STI,1)
  ;get this vertex1 position
  X# = VertexX(SSurface,SV1I) : Y# = VertexY(SSurface,SV1I) : Z# = VertexZ(SSurface,SV1I)
  ;check if there is already a vertex existing at this position in the destination surface (with a tolerance of 0.01unit)
  TVI% = -1
  For DVI% = 0 To CountVertices(DSurface)-1 Step 1
   If( VertexX(DSurface,DVI) >= X-0.01 And VertexX(DSurface,DVI) <= X+0.01 And VertexY(DSurface,DVI) >= Y-0.01 And VertexY(DSurface,DVI) <= Y+0.01 And VertexZ(DSurface,DVI) >= Z-0.01 And VertexZ(DSurface,DVI) <= Z+0.01 )
    TVI = DVI
    Exit
   EndIf
  Next
  ;if yes
  If( TVI <> - 1 )
   ;use this vertex
   DV1I% = TVI
  ;if no
  Else If( TVI% = - 1 )
   ;create a new vertex in the destination surface
   DVerticesCount = DVerticesCount + 1
   DV1I% = DVerticesCount - 1
   AddVertex(DSurface,X,Y,Z)
  EndIf

  ;for vertex2 of this triangle
  SV2I% = TriangleVertex(SSurface,STI,2)
  ;get this vertex0 position
  X# = VertexX(SSurface,SV2I) : Y# = VertexY(SSurface,SV2I) : Z# = VertexZ(SSurface,SV2I)
  ;check if there is already a vertex existing at this position in the destination surface (with a tolerance of 0.01unit)
  TVI% = -1
  For DVI% = 0 To CountVertices(DSurface)-1 Step 1
   If( VertexX(DSurface,DVI) >= X-0.01 And VertexX(DSurface,DVI) <= X+0.01 And VertexY(DSurface,DVI) >= Y-0.01 And VertexY(DSurface,DVI) <= Y+0.01 And VertexZ(DSurface,DVI) >= Z-0.01 And VertexZ(DSurface,DVI) <= Z+0.01 )
    TVI = DVI
    Exit
   EndIf
  Next
  ;if yes
  If( TVI <> - 1 )
   ;use this vertex
   DV2I% = TVI
  ;if no
  Else If( TVI% = - 1 )
   ;create a new vertex in the destination surface
   DVerticesCount = DVerticesCount + 1
   DV2I% = DVerticesCount - 1
   AddVertex(DSurface,X,Y,Z)
  EndIf

  ;create a new triangle in the destination surface
  AddTriangle(DSurface,DV0I,DV1I,DV2I)
 
Next

UpdateNormals(DMesh)

;add some irregularities to the surface
For DVI% = 0 To CountVertices(DSurface)-1 Step 1
  X# = VertexX(DSurface,DVI) : Y# = VertexY(DSurface,DVI) : Z# = VertexZ(DSurface,DVI)
  NX# = VertexNX(DSurface,DVI) : NY# = VertexNY(DSurface,DVI) : NZ# = VertexNZ(DSurface,DVI)
  NewX# = X + NX*Rnd(0.1,MaxIrregularity) : NewY# = Y + NY*Rnd(0.1,MaxIrregularity) : NewZ# = Z + NZ*Rnd(0.1,MaxIrregularity)
  VertexCoords(DSurface,DVI,NewX,NewY,NewZ)
Next

DebugLog(CountVertices(SSurface)+" "+CountTriangles(SSurface))
DebugLog(CountVertices(DSurface)+" "+CountTriangles(DSurface))

FreeEntity(SMesh)

UpdateNormals(DMesh)

Return DMesh

End Function

Function CreateTree()

DMesh = CreateMesh()

;trunc
CreateSurface(DMesh)
TPart= CreateCylinder(8)
ScaleMesh(TPart,0.3/2,3.0/2,0.3/2)
PositionMesh(TPart,0,3.0/2,0)
For SI% = 1 To CountSurfaces(TPart) Step 1
  AddSurfaceToOtherSurface(GetSurface(TPart,SI),TPart,GetSurface(DMesh,1),DMesh)
Next
FreeEntity(TPart)
ColorWithBrush(GetSurface(DMesh,1),100,050,000)

;leaves
CreateSurface(DMesh)
TPart = CreateCone(8)
ScaleMesh(TPart,3.0/2,6.0/2,3.0/2)
PositionMesh(TPart,0,3.0+6.0/2,0)
For SI% = 1 To CountSurfaces(TPart) Step 1
  AddSurfaceToOtherSurface(GetSurface(TPart,SI),TPart,GetSurface(DMesh,2),DMesh)
Next
FreeEntity(TPart)
ColorWithBrush(GetSurface(DMesh,2),000,100,000)

Return DMesh

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

;vertices+triangles (with normals, with colors, with alphas, with uvs)
Function AddSurfaceToOtherSurface(Surface,Mesh,OSurface,OMesh)

SurfaceVerticesCount% = CountVertices(Surface)
;DebugLog("SurfaceVerticesCount = "+SurfaceVerticesCount)
OSurfaceVerticesCount% = CountVertices(OSurface)
;DebugLog("OSurfaceVerticesCount = "+OSurfaceVerticesCount)
For VI% = 0 To CountVertices(Surface)-1 Step 1
  ;DebugLog("VI = "+VI)
  VLX# = VertexX(Surface,VI)
  VLY# = VertexY(Surface,VI)
  VLZ# = VertexZ(Surface,VI)
  TFormPoint(VLX,VLY,VLZ,Mesh,0)
  VGX# = TFormedX()
  VGY# = TFormedY()
  VGZ# = TFormedZ()
  VNX# = VertexNX(Surface,VI)
  VNY# = VertexNY(Surface,VI)
  VNZ# = VertexNZ(Surface,VI)
  VR% = VertexRed(Surface,VI)
  VG% = VertexGreen(Surface,VI)
  VB% = VertexBlue(Surface,VI)
  VA# = VertexAlpha(Surface,VI)
  VU# = VertexU(Surface,VI,0)
  VV# = VertexV(Surface,VI,0)
  If( OSurfaceVerticesCount = 0 )
   NVI = VI
  Else If( OSurfaceVerticesCount > 0 )
   NVI% = OSurfaceVerticesCount+VI
  EndIf
  ;DebugLog("NVI = "+NVI)
  AddVertex(OSurface,VGX,VGY,VGZ)
  VertexNormal(OSurface,NVI,VNX,VNY,VNZ)
  VertexColor(OSurface,NVI,VR,VG,VB,VA)
  VertexTexCoords(OSurface,NVI,VU,VV)
  ;WaitKey()
Next
SurfaceTrianglesCount% = CountTriangles(Surface)
;DebugLog("SurfaceTrianglesCount = "+SurfaceTrianglesCount)
OSurfaceTrianglesCount% = CountTriangles(OSurface)
;DebugLog("OSurfaceTrianglesCount = "+OSurfaceTrianglesCount)
For TI% = 0 To CountTriangles(Surface)-1 Step 1
  V0I% = TriangleVertex(Surface,TI,0) ;vertex0
  V1I% = TriangleVertex(Surface,TI,1) ;vertex1
  V2I% = TriangleVertex(Surface,TI,2) ;vertex2
  ;DebugLog("oldtriangle"+TI+" "+V0I+","+V1I+","+V2I)
  If( OSurfaceVerticesCount = 0 )
   NV0I% = V0I
   NV1I% = V1I
   NV2I% = V2I
  Else If( OSurfaceVerticesCount > 0 )
   NV0I% = OSurfaceVerticesCount+V0I
   NV1I% = OSurfaceVerticesCount+V1I
   NV2I% = OSurfaceVerticesCount+V2I
  EndIf
  ;DebugLog("newtriangle"+TI+" "+NV0I+","+NV1I+","+NV2I)
  AddTriangle(OSurface,NV0I,NV1I,NV2I)
Next
;WaitKey()

End Function

Function ColorWithBrush(Surface,R%,G%,B%,A#=1.0,Fx%=0,BlendMode%=1)

Brush = CreateBrush()
BrushColor(Brush,R,G,B)
BrushAlpha(Brush,A)
BrushFX(Brush,Fx)
BrushBlend(Brush,BlendMode)
PaintSurface(Surface,Brush)
FreeBrush(Brush)

End Function

Function AddGround()

GroundShape = CreateCube()
ScaleMesh(GroundShape,100.0/2,1.0/2,100.0/2)
PositionMesh(GroundShape,100.0/2,-1.0/2,100.0/2)
EntityColor(GroundShape,050,150,050)
NameEntity(GroundShape,"GRO"+Str(1))

End Function

Function AddRocks()

For n% = 1 To 15 Step 1
  ;choose a position
  .LineChooseRockPosition
  X# = Rnd(0+1.0,100-1.0) : Z# = Rnd(0+1.0,100-1.0)
  ;check if this position is far enough from others existing rocks
  For rr.Rock = Each Rock
   D# = Distance2D(X,Z,EntityX(rr\Shape,True),EntityZ(rr\Shape,True))
   If( D < 1.0+1.0+1.0 )
    Goto LineChooseRockPosition ;i have not added a check to exit the loop in case there are no more empty space, so don't add too many rocks !!!
   EndIf
  Next
  r.Rock = New Rock
  r\Shape = CopyEntity(XRock)
  Size# = Rnd(0.5,2.0)
  ScaleEntity(r\Shape,Size,Size,Size)
  PositionEntity(r\Shape,X,0,Z)
  NameEntity(r\Shape,"ROC"+Handle(r))
Next

End Function

Function AddTrees()

For n% = 1 To 60 Step 1
  ;choose a position
  .LineChooseTreePosition
  X# = Rnd(0+1.5,100-1.5) : Z# = Rnd(0+1.5,100-1.5)
  ;check if this position is far enough from others existing rocks
  For rr.Rock = Each Rock
   D# = Distance2D(X,Z,EntityX(rr\Shape,True),EntityZ(rr\Shape,True))
   If( D < 2.0+1.5+1.0 )
    Goto LineChooseTreePosition ;i have not added a check to exit the loop in case there are no more empty space, so don't add too many trees !!!
   EndIf
  Next
  ;check if this position is far enough from others existing trees
  For tt.Tree = Each Tree
   D# = Distance2D(X,Z,EntityX(tt\Shape,True),EntityZ(tt\Shape,True))
   If( D < 1.5+1.5+1.0 )
    Goto LineChooseTreePosition ;i have not added a check to exit the loop in case there are no more empty space, so don't add too many trees !!!
   EndIf
  Next
  t.Tree = New Tree
  t\Shape = CopyEntity(XTree)
  Size# = Rnd(1.0,3.0)
  ScaleEntity(t\Shape,Size,Size,Size)
  PositionEntity(t\Shape,X,0,Z)
  NameEntity(t\Shape,"TRE"+Handle(t))
Next

End Function

Function AddBots()

For n% = 1 To 10 Step 1
  ;choose a position
  .LineChooseBotPosition
  X# = Rnd(0+0.25,100-0.25) : Z# = Rnd(0+0.25,100-0.25)
  ;check if this position is far enough from others existing rocks
  For rr.Rock = Each Rock
   D# = Distance2D(X,Z,EntityX(rr\Shape,True),EntityZ(rr\Shape,True))
   If( D < 2.0+0.25+1.0 )
    Goto LineChooseBotPosition ;i have not added a check to exit the loop in case there are no more empty space, so don't add too many bots !!!
   EndIf
  Next
  ;check if this position is far enough from others existing trees
  For tt.Tree = Each Tree
   D# = Distance2D(X,Z,EntityX(tt\Shape,True),EntityZ(tt\Shape,True))
   If( D < 1.5+0.25+1.0 )
    Goto LineChooseBotPosition ;i have not added a check to exit the loop in case there are no more empty space, so don't add too many bots !!!
   EndIf
  Next
  ;check if this position is far enough from others existing bots
  For bb.Bot = Each Bot
   D# = Distance2D(X,Z,EntityX(bb\Shape,True),EntityZ(bb\Shape,True))
   If( D < 0.25+0.25+1.0 )
    Goto LineChooseBotPosition ;i have not added a check to exit the loop in case there are no more empty space, so don't add too many bots !!!
   EndIf
  Next
  b.Bot = New Bot
  b\Shape = CopyEntity(XBot)
  PositionEntity(b\Shape,X,0,Z)
  b\Life = 1.0
  b\PrevMS = MilliSecs()
  NameEntity(b\Shape,"BOT"+Handle(b))
Next

End Function

Function AddPlayer()

PlayerShape = CopyEntity(XPlayer)
PositionEntity(PlayerShape,50,0,50)
PlayerLife = 1.0
PlayerPrevMS = MilliSecs()
NameEntity(PlayerShape,"PLA"+Str(1))

End Function

Function UpdatePlayer()

;turn left/right
MoveMouse(GraphicsWidth()/2,GraphicsHeight()/2)
PlayerYaw = PlayerYaw - Float(MXDiff)/10
RotateEntity(PlayerShape,0,PlayerYaw,0,False)
;move forward/backward/left/right
If( KeyDown(17)=0 And KeyDown(31)=0 And KeyDown(30)=0 And KeyDown(32)=0 )
  TFormVector(0,0,0,PlayerShape,0)
Else If( KeyDown(17)=1 )
  TFormVector(0,0,+0.347,PlayerShape,0) ;player speed is 0.347
Else If( KeyDown(31)=1 )
  TFormVector(0,0,-0.347,PlayerShape,0) ;player speed is 0.347
Else If( KeyDown(30)=1 ) 
  TFormVector(-0.347,0,0,PlayerShape,0) ;player speed is 0.347
Else If( KeyDown(32)=1 )
  TFormVector(+0.347,0,0,PlayerShape,0) ;player speed is 0.347
EndIf
;calculates the movement vector
PlayerVX = TFormedX() : PlayerVY = TFormedY() : PlayerVZ = TFormedZ()
;before moving player, check if it will collides with an obstacle entity (tree or bot)
TFormPoint(0,0.25,0,PlayerShape,0) ;root of Player shape +0.25 (on the Y axis)
LinePick(TFormedX(),TFormedY(),TFormedZ(),PlayerVX,PlayerVY,PlayerVZ,0.25-0.01) ;player ellipsoid radius is 0.25 ;throws a linepick from the position of player root +0.25 to direction of the movement vector
EntRef% = PickedEntity()
;if yes
If( EntRef <> 0 )
  ;debug collision point / normal
  PositionEntity(PickPoint,PickedX(),PickedY(),PickedZ())
  PositionEntity(PickNormal,PickedX(),PickedY(),PickedZ()) : AlignToVector(PickNormal,PickedNX(),PickedNY(),PickedNZ(),3,1.0)
  ;recalculate the appropriate position
  PlayerX# = EntityX(PlayerShape,True) : PlayerY# = EntityY(PlayerShape,True) : PlayerZ# = EntityZ(PlayerShape,True)
  PickX# = PickedX() : PickY# = PickedZ() : PickZ# = PickedZ()
  D# = Distance2D(PlayerX,PlayerZ,PickX,PickZ)
  TCoeff# = (D-0.25)/(0.347)
  PlayerVX = PlayerVX*TCoeff : PlayerVZ = PlayerVZ*TCoeff
  ;move player
  TranslateEntity(PlayerShape,PlayerVX,PlayerVY,PlayerVZ)
;if no
Else If( EntRef = 0 )
  ;move player
  TranslateEntity(PlayerShape,PlayerVX,PlayerVY,PlayerVZ)
EndIf

;shoot a bullet
If( MouseHit(1)=1 )
  NowMS% = MilliSecs()
  ;allow to shoot a new bullet only if the previous bullet was shot more than 100ms ago (1000milliseconds = 1second)
  If( NowMS - PlayerPrevMS > 100 )
   ;create a new bullet
   BulletsCount = BulletsCount + 1
   bu.bullet = New bullet
   bu\Shape = CopyEntity(XBullet)
   PositionEntity(bu\Shape,EntityX(PlayerShape,True),EntityY(PlayerShape,True)+0.875,EntityZ(PlayerShape,True))
   RotateEntity(bu\Shape,EntityPitch(PlayerShape,True),EntityYaw(PlayerShape,True),EntityRoll(PlayerShape,True))
   TurnEntity(bu\Shape,Rand(-1,+1),Rand(-1,+1),0)
   bu\Life# = 1.0
   PlayerPrevMS = NowMS
  EndIf
EndIf

End Function

Function UpdateBots()

For bo.Bot = Each Bot
  ;depending on ai (wander, pursue and attack, flee and hide), decide how bot will move / act
Next

End Function

Function UpdateBullets()

For bu.Bullet = Each Bullet
  ;decrease bullet life so that it does not last forever
  bu\Life = bu\Life - 0.01
  ;if life is more than 0, update the bullet
  If( bu\Life > 0 )
   ;calculates the movement vector
   TFormVector(0,0,+1.2,bu\Shape,0) ;bullet speed is 1.2
   ;calculates the movement vector
   bu\VX = TFormedX() : bu\VY = TFormedY() : bu\VZ = TFormedZ()
   ;before moving the bullet, check if it will collides with an obstacle entity (tree or player or bot)
   TFormPoint(0,0,0,bu\Shape,0) ;root of bullet shape
   LinePick(TFormedX(),TFormedY(),TFormedZ(),bu\VX,bu\VY,bu\VZ,0.1) ;bullet ellipsoid radius is 0.1 ;throws a linepick from the position of bullet +0 to direction of the movement vector 
   EntRef% = PickedEntity()
   ;if yes
   If( EntRef <> 0 )
    ;recalculate the appropriate position
    BulletX# = EntityX(bu\Shape,True) : BulletY# = EntityY(bu\Shape,True) : BulletZ# = EntityZ(bu\Shape,True)
    PickX# = PickedX() : PickY# = PickedY() : PickZ# = PickedZ()
    PickNX# = PickedNX() : PickNY# = PickedNY() : PickNZ# = PickedNZ()
    D# = Distance2D(BulletX,BulletZ,PickX,PickZ)
    TCoeff# = (D-0.1)/(1.2)
    bu\VX = bu\VX*TCoeff : bu\VZ = bu\VZ*TCoeff
    ;move bullet
    TranslateEntity(bu\Shape,bu\VX,bu\VY,bu\VZ)
    ;destroy the bullet
    FreeEntity(bu\Shape) : Delete(bu)
    BulletsCount = BulletsCount - 1
    ;get kind of the picked entity
    EntName$ = EntityName(EntRef) : EntKind$ = Left(EntName,3) : EntH% = Right(EntName,Len(EntName)-3)
    If( EntKind = "GRO" )
     MaterialR% = 050 : MaterialG% = 150 : MaterialB% = 050
    Else If( EntKind = "ROC" )
     MaterialR% = 125 : MaterialG% = 125 : MaterialB% = 125
    Else If( EntKind = "TRE" )
     MaterialR% = 100 : MaterialG% = 050 : MaterialB% = 000
    Else If( EntKind = "BOT" )
     MaterialR% = 125 : MaterialG% = 125 : MaterialB% = 255
    Else If( EntKind = "PLA" )
     MaterialR% = 000 : MaterialG% = 000 : MaterialB% = 255
    EndIf
    ;create some particles
    For n% = 1 To 16 Step 1
     pa.Particle = New Particle : ParticlesCount = ParticlesCount + 1
     pa\Shape = CopyEntity(XParticle)
     EntityColor(pa\Shape,MaterialR,MaterialG,MaterialB)
     PositionEntity(pa\Shape,PickX,PickY,PickZ,True)
     AlignToVector(pa\Shape,PickNX,PickNY,PickNZ,3,1.0) : TurnEntity(pa\Shape,Rand(-45,+45),Rand(-45,+45),0)
     pa\Life# = 1.0
    Next
   ;if no
   Else If( EntRef = 0 )
    ;move the bullet
    TranslateEntity(bu\Shape,bu\VX,bu\VY,bu\VZ)
   EndIf
  ;if life is equal or less than 0, destroy the bullet
  Else If( bu\Life <= 0 )
   FreeEntity(bu\Shape) : Delete(bu)
   BulletsCount = BulletsCount - 1
  EndIf
Next

End Function

Function UpdateParticles()

For pa.Particle = Each Particle
  MoveEntity(pa\Shape,0,0,0.03)
  pa\Life = pa\Life - 0.03
  EntityAlpha(pa\Shape,pa\Life)
  If( pa\Life <= 0 )
   FreeEntity(pa\Shape) : Delete(pa)
   ParticlesCount = ParticlesCount - 1
  EndIf
Next

End Function


RemiD

same world / entities / gameplay, but this time the code is modified in a way to have the collisions detection / repositionning with ellipsoids and collidable shapes (Blitz3d collision system)


Graphics3D(1000,625,32,2)

HidePointer()

SeedRnd(MilliSecs())

;----------premade shapes----------
Global XRock = CreateRock(8,0.4)
ScaleMesh(XRock,2.0/2,1.5/2,2.0/2)
PositionMesh(XRock,0,1.5/2-0.5,0)
EntityColor(XRock,125,125,125)

Global XTree = CreateTree()
HideEntity(XTree)

Global XPlayer = CreateCylinder(16)
ScaleMesh(XPlayer,0.5/2,1.75/2,0.5/2)
PositionMesh(XPlayer,0,0.875,0)
EntityColor(XPlayer,000,000,255)
HideEntity(XPlayer)

Global XBot = CreateCylinder(16)
ScaleMesh(XBot,0.5/2,1.75/2,0.5/2)
PositionMesh(XBot,0,0.875,0)
EntityColor(XBot,125,125,255)
HideEntity(XBot)

Global XBullet = CreateSphere(8)
ScaleMesh(XBullet,0.2/2,0.2/2,0.2/2)
EntityColor(XBullet,125,125,125)
HideEntity(XBullet)

Global XParticle = CreateCube()
ScaleMesh(XParticle,0.04/2,0.04/2,0.04/2)
EntityColor(XParticle,125,125,125)
HideEntity(XParticle)

;-----------------------------------

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

;input
Global MXDiff%
Global MYDiff%

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

;ground
Global GroundShape
AddGround()

;rocks
Type Rock
Field Shape
End Type
AddRocks()

;trees
Type Tree
Field Shape
End Type
AddTrees()

;bots
Type Bot
Field Ellipsoid
Field Shape
Field Life#
Field PrevMS%
End Type
AddBots()

;player
Global PlayerEllipsoid
Global PlayerShape
Global PlayerYaw#
Global PlayerLife#
Global PlayerPrevMS%
AddPlayer()

;bullets
Type Bullet
Field Ellipsoid
Field Shape
Field Life#
End Type

;particles
Type Particle
Field Shape
Field Life#
End Type

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

AmbientLight(064,064,064)

;setup collision groups (do not confuse "EntityType()" which define a group of entities and "Type End Type" which define a customtype variable !!! )
Const GShape% = 1
Const GTurningMoving% = 2

EntityType(GroundShape,GShape)
For r.Rock = Each Rock
EntityType(r\Shape,GShape)
Next
For t.Tree = Each Tree
EntityType(t\Shape,GShape)
Next
For bo.Bot = Each Bot
EntityType(bo\Shape,GShape) : EntityType(bo\Ellipsoid,GTurningMoving)
Next
EntityType(PlayerShape,GShape) : EntityType(PlayerEllipsoid,GTurningMoving)

;setup collidables entities and collisions detection / response
Collisions(GTurningMoving,GShape,2,1)


EntityPickMode(GroundShape,2)
For r.Rock = Each Rock
EntityPickMode(r\Shape,2)
Next
For t.Tree = Each tree
EntityPickMode(t\Shape,2)
Next
For b.Bot = Each Bot
EntityPickMode(b\Shape,2) :
Next
EntityPickMode(PlayerShape,2) :

Global CollPoint = CreateCube()
ScaleMesh(CollPoint,0.03/2,0.03/2,0.03/2)
EntityColor(CollPoint,255,000,255)
EntityFX(CollPoint,1)

Global CollNormal = CreateCube()
ScaleMesh(CollNormal,0.01/2,0.01/2,0.3/2)
PositionMesh(CollNormal,0,0,0.3/2)
EntityColor(CollNormal,125,000,125)
EntityFX(CollNormal,1)

PositionEntity(Camera,EntityX(PlayerShape,True),EntityY(PlayerShape,True),EntityZ(PlayerShape,True))
MoveEntity(Camera,0,1.65+1.5,-3.0)
TurnEntity(Camera,22.5,0,0)
EntityParent(Camera,PlayerShape)

Global BulletsCount% = 0 ;to track how many bullets are active in the scene
Global ParticlesCount% = 0 ;to track how many particles are active in the scene

Global MainLoopTimer = CreateTimer(30) ;to lock the FPS at 30FPS max

;mainloop
Main()

End()

Function Main()

Repeat

  MXDiff = MouseXSpeed()
  MYDiff = MouseYSpeed()

  ;update player before collisions (depending on input mouse/keyboard)
  UpdatePlayerBC()

  ;update bots before collisions (depending on ai)
  UpdateBotsBC()

  ;update bullets before collisions
  UpdateBulletsBC()

  ;detect collisions + response (reposition)
  UpdateWorld()

  ;update player after collisions
  UpdatePlayerAC()

  ;update bots after collisions
  UpdateBotsAC()

  ;update bullets after collisions
  UpdateBulletsAC()

  ;update particles
  UpdateParticles()

  WireFrame(False)
  If( KeyDown(2)=True )
   WireFrame(True)
  EndIf

  ;render
  RenderWorld()

  Text(0,0,"BulletsCount = "+BulletsCount)
  Text(0,16,"ParticlesCount = "+ParticlesCount)

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

Until( KeyDown(1)=1 )

End Function

Function CreateRock(Details%=8,MaxIrregularity#=0.3)

SMesh = CreateSphere(Details)
SSurface = GetSurface(SMesh,1)

DMesh = CreateMesh()
DSurface = CreateSurface(DMesh)
DVerticesCount% = 0

;for each triangle of the source surface
For STI% = 0 To CountTriangles(SSurface)-1 Step 1

  ;for vertex0 of this triangle
  SV0I% = TriangleVertex(SSurface,STI,0)
  ;get this vertex0 position
  X# = VertexX(SSurface,SV0I) : Y# = VertexY(SSurface,SV0I) : Z# = VertexZ(SSurface,SV0I)
  ;check if there is already a vertex existing at this position in the destination surface (with a tolerance of 0.01unit)
  TVI% = -1
  For DVI% = 0 To CountVertices(DSurface)-1 Step 1
   If( VertexX(DSurface,DVI) >= X-0.01 And VertexX(DSurface,DVI) <= X+0.01 And VertexY(DSurface,DVI) >= Y-0.01 And VertexY(DSurface,DVI) <= Y+0.01 And VertexZ(DSurface,DVI) >= Z-0.01 And VertexZ(DSurface,DVI) <= Z+0.01 )
    TVI = DVI
    Exit
   EndIf
  Next
  ;if yes
  If( TVI <> - 1 )
   ;use this vertex
   DV0I% = TVI
  ;if no
  Else If( TVI% = - 1 )
   ;create a new vertex in the destination surface
   DVerticesCount = DVerticesCount + 1
   DV0I% = DVerticesCount - 1
   AddVertex(DSurface,X,Y,Z)
  EndIf

  ;for vertex1 of this triangle
  SV1I% = TriangleVertex(SSurface,STI,1)
  ;get this vertex1 position
  X# = VertexX(SSurface,SV1I) : Y# = VertexY(SSurface,SV1I) : Z# = VertexZ(SSurface,SV1I)
  ;check if there is already a vertex existing at this position in the destination surface (with a tolerance of 0.01unit)
  TVI% = -1
  For DVI% = 0 To CountVertices(DSurface)-1 Step 1
   If( VertexX(DSurface,DVI) >= X-0.01 And VertexX(DSurface,DVI) <= X+0.01 And VertexY(DSurface,DVI) >= Y-0.01 And VertexY(DSurface,DVI) <= Y+0.01 And VertexZ(DSurface,DVI) >= Z-0.01 And VertexZ(DSurface,DVI) <= Z+0.01 )
    TVI = DVI
    Exit
   EndIf
  Next
  ;if yes
  If( TVI <> - 1 )
   ;use this vertex
   DV1I% = TVI
  ;if no
  Else If( TVI% = - 1 )
   ;create a new vertex in the destination surface
   DVerticesCount = DVerticesCount + 1
   DV1I% = DVerticesCount - 1
   AddVertex(DSurface,X,Y,Z)
  EndIf

  ;for vertex2 of this triangle
  SV2I% = TriangleVertex(SSurface,STI,2)
  ;get this vertex0 position
  X# = VertexX(SSurface,SV2I) : Y# = VertexY(SSurface,SV2I) : Z# = VertexZ(SSurface,SV2I)
  ;check if there is already a vertex existing at this position in the destination surface (with a tolerance of 0.01unit)
  TVI% = -1
  For DVI% = 0 To CountVertices(DSurface)-1 Step 1
   If( VertexX(DSurface,DVI) >= X-0.01 And VertexX(DSurface,DVI) <= X+0.01 And VertexY(DSurface,DVI) >= Y-0.01 And VertexY(DSurface,DVI) <= Y+0.01 And VertexZ(DSurface,DVI) >= Z-0.01 And VertexZ(DSurface,DVI) <= Z+0.01 )
    TVI = DVI
    Exit
   EndIf
  Next
  ;if yes
  If( TVI <> - 1 )
   ;use this vertex
   DV2I% = TVI
  ;if no
  Else If( TVI% = - 1 )
   ;create a new vertex in the destination surface
   DVerticesCount = DVerticesCount + 1
   DV2I% = DVerticesCount - 1
   AddVertex(DSurface,X,Y,Z)
  EndIf

  ;create a new triangle in the destination surface
  AddTriangle(DSurface,DV0I,DV1I,DV2I)
 
Next

UpdateNormals(DMesh)

;add some irregularities to the surface
For DVI% = 0 To CountVertices(DSurface)-1 Step 1
  X# = VertexX(DSurface,DVI) : Y# = VertexY(DSurface,DVI) : Z# = VertexZ(DSurface,DVI)
  NX# = VertexNX(DSurface,DVI) : NY# = VertexNY(DSurface,DVI) : NZ# = VertexNZ(DSurface,DVI)
  NewX# = X + NX*Rnd(0.1,MaxIrregularity) : NewY# = Y + NY*Rnd(0.1,MaxIrregularity) : NewZ# = Z + NZ*Rnd(0.1,MaxIrregularity)
  VertexCoords(DSurface,DVI,NewX,NewY,NewZ)
Next

DebugLog(CountVertices(SSurface)+" "+CountTriangles(SSurface))
DebugLog(CountVertices(DSurface)+" "+CountTriangles(DSurface))

FreeEntity(SMesh)

UpdateNormals(DMesh)

Return DMesh

End Function

Function CreateTree()

DMesh = CreateMesh()

;trunc
CreateSurface(DMesh)
TPart= CreateCylinder(8)
ScaleMesh(TPart,0.3/2,3.0/2,0.3/2)
PositionMesh(TPart,0,3.0/2,0)
For SI% = 1 To CountSurfaces(TPart) Step 1
  AddSurfaceToOtherSurface(GetSurface(TPart,SI),TPart,GetSurface(DMesh,1),DMesh)
Next
FreeEntity(TPart)
ColorWithBrush(GetSurface(DMesh,1),100,050,000)

;leaves
CreateSurface(DMesh)
TPart = CreateCone(8)
ScaleMesh(TPart,3.0/2,6.0/2,3.0/2)
PositionMesh(TPart,0,3.0+6.0/2,0)
For SI% = 1 To CountSurfaces(TPart) Step 1
  AddSurfaceToOtherSurface(GetSurface(TPart,SI),TPart,GetSurface(DMesh,2),DMesh)
Next
FreeEntity(TPart)
ColorWithBrush(GetSurface(DMesh,2),000,100,000)

Return DMesh

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

;vertices+triangles (with normals, with colors, with alphas, with uvs)
Function AddSurfaceToOtherSurface(Surface,Mesh,OSurface,OMesh)

SurfaceVerticesCount% = CountVertices(Surface)
;DebugLog("SurfaceVerticesCount = "+SurfaceVerticesCount)
OSurfaceVerticesCount% = CountVertices(OSurface)
;DebugLog("OSurfaceVerticesCount = "+OSurfaceVerticesCount)
For VI% = 0 To CountVertices(Surface)-1 Step 1
  ;DebugLog("VI = "+VI)
  VLX# = VertexX(Surface,VI)
  VLY# = VertexY(Surface,VI)
  VLZ# = VertexZ(Surface,VI)
  TFormPoint(VLX,VLY,VLZ,Mesh,0)
  VGX# = TFormedX()
  VGY# = TFormedY()
  VGZ# = TFormedZ()
  VNX# = VertexNX(Surface,VI)
  VNY# = VertexNY(Surface,VI)
  VNZ# = VertexNZ(Surface,VI)
  VR% = VertexRed(Surface,VI)
  VG% = VertexGreen(Surface,VI)
  VB% = VertexBlue(Surface,VI)
  VA# = VertexAlpha(Surface,VI)
  VU# = VertexU(Surface,VI,0)
  VV# = VertexV(Surface,VI,0)
  If( OSurfaceVerticesCount = 0 )
   NVI = VI
  Else If( OSurfaceVerticesCount > 0 )
   NVI% = OSurfaceVerticesCount+VI
  EndIf
  ;DebugLog("NVI = "+NVI)
  AddVertex(OSurface,VGX,VGY,VGZ)
  VertexNormal(OSurface,NVI,VNX,VNY,VNZ)
  VertexColor(OSurface,NVI,VR,VG,VB,VA)
  VertexTexCoords(OSurface,NVI,VU,VV)
  ;WaitKey()
Next
SurfaceTrianglesCount% = CountTriangles(Surface)
;DebugLog("SurfaceTrianglesCount = "+SurfaceTrianglesCount)
OSurfaceTrianglesCount% = CountTriangles(OSurface)
;DebugLog("OSurfaceTrianglesCount = "+OSurfaceTrianglesCount)
For TI% = 0 To CountTriangles(Surface)-1 Step 1
  V0I% = TriangleVertex(Surface,TI,0) ;vertex0
  V1I% = TriangleVertex(Surface,TI,1) ;vertex1
  V2I% = TriangleVertex(Surface,TI,2) ;vertex2
  ;DebugLog("oldtriangle"+TI+" "+V0I+","+V1I+","+V2I)
  If( OSurfaceVerticesCount = 0 )
   NV0I% = V0I
   NV1I% = V1I
   NV2I% = V2I
  Else If( OSurfaceVerticesCount > 0 )
   NV0I% = OSurfaceVerticesCount+V0I
   NV1I% = OSurfaceVerticesCount+V1I
   NV2I% = OSurfaceVerticesCount+V2I
  EndIf
  ;DebugLog("newtriangle"+TI+" "+NV0I+","+NV1I+","+NV2I)
  AddTriangle(OSurface,NV0I,NV1I,NV2I)
Next
;WaitKey()

End Function

Function ColorWithBrush(Surface,R%,G%,B%,A#=1.0,Fx%=0,BlendMode%=1)

Brush = CreateBrush()
BrushColor(Brush,R,G,B)
BrushAlpha(Brush,A)
BrushFX(Brush,Fx)
BrushBlend(Brush,BlendMode)
PaintSurface(Surface,Brush)
FreeBrush(Brush)

End Function

Function AddGround()

GroundShape = CreateCube()
ScaleMesh(GroundShape,100.0/2,1.0/2,100.0/2)
PositionMesh(GroundShape,100.0/2,-1.0/2,100.0/2)
EntityColor(GroundShape,050,150,050)
NameEntity(GroundShape,"GRO"+Str(1))

End Function

Function AddRocks()

For n% = 1 To 15 Step 1
  ;choose a position
  .LineChooseRockPosition
  X# = Rnd(0+1.0,100-1.0) : Z# = Rnd(0+1.0,100-1.0)
  ;check if this position is far enough from others existing rocks
  For rr.Rock = Each Rock
   D# = Distance2D(X,Z,EntityX(rr\Shape,True),EntityZ(rr\Shape,True))
   If( D < 1.0+1.0+1.0 )
    Goto LineChooseRockPosition ;i have not added a check to exit the loop in case there are no more empty space, so don't add too many rocks !!!
   EndIf
  Next
  r.Rock = New Rock
  r\Shape = CopyEntity(XRock)
  Size# = Rnd(0.5,2.0)
  ScaleEntity(r\Shape,Size,Size,Size)
  PositionEntity(r\Shape,X,0,Z)
  NameEntity(r\Shape,"ROC"+Handle(r))
Next

End Function

Function AddTrees()

For n% = 1 To 60 Step 1
  ;choose a position
  .LineChooseTreePosition
  X# = Rnd(0+1.5,100-1.5) : Z# = Rnd(0+1.5,100-1.5)
  ;check if this position is far enough from others existing rocks
  For rr.Rock = Each Rock
   D# = Distance2D(X,Z,EntityX(rr\Shape,True),EntityZ(rr\Shape,True))
   If( D < 2.0+1.5+1.0 )
    Goto LineChooseTreePosition ;i have not added a check to exit the loop in case there are no more empty space, so don't add too many trees !!!
   EndIf
  Next
  ;check if this position is far enough from others existing trees
  For tt.Tree = Each Tree
   D# = Distance2D(X,Z,EntityX(tt\Shape,True),EntityZ(tt\Shape,True))
   If( D < 1.5+1.5+1.0 )
    Goto LineChooseTreePosition ;i have not added a check to exit the loop in case there are no more empty space, so don't add too many trees !!!
   EndIf
  Next
  t.Tree = New Tree
  t\Shape = CopyEntity(XTree)
  Size# = Rnd(1.0,3.0)
  ScaleEntity(t\Shape,Size,Size,Size)
  PositionEntity(t\Shape,X,0,Z)
  NameEntity(t\Shape,"TRE"+Handle(t))
Next

End Function

Function AddBots()

For n% = 1 To 10 Step 1
  ;choose a position
  .LineChooseBotPosition
  X# = Rnd(0+0.25,100-0.25) : Z# = Rnd(0+0.25,100-0.25)
  ;check if this position is far enough from others existing rocks
  For rr.Rock = Each Rock
   D# = Distance2D(X,Z,EntityX(rr\Shape,True),EntityZ(rr\Shape,True))
   If( D < 2.0+0.25+1.0 )
    Goto LineChooseBotPosition ;i have not added a check to exit the loop in case there are no more empty space, so don't add too many bots !!!
   EndIf
  Next
  ;check if this position is far enough from others existing trees
  For tt.Tree = Each Tree
   D# = Distance2D(X,Z,EntityX(tt\Shape,True),EntityZ(tt\Shape,True))
   If( D < 1.5+0.25+1.0 )
    Goto LineChooseBotPosition ;i have not added a check to exit the loop in case there are no more empty space, so don't add too many bots !!!
   EndIf
  Next
  ;check if this position is far enough from others existing bots
  For bb.Bot = Each Bot
   D# = Distance2D(X,Z,EntityX(bb\Shape,True),EntityZ(bb\Shape,True))
   If( D < 0.25+0.25+1.0 )
    Goto LineChooseBotPosition ;i have not added a check to exit the loop in case there are no more empty space, so don't add too many bots !!!
   EndIf
  Next
  b.Bot = New Bot
  b\Shape = CopyEntity(XBot)
  b\Ellipsoid = CreatePivot()
  EntityRadius(b\Ellipsoid,0.24)
  PositionEntity(b\Ellipsoid,X,0.24+0.1,Z)
  b\Life = 1.0
  b\PrevMS = MilliSecs()
  NameEntity(b\Shape,"BOT"+Handle(b))
Next

End Function

Function AddPlayer()

PlayerShape = CopyEntity(XPlayer)
PlayerEllipsoid = CreatePivot()
EntityRadius(PlayerEllipsoid,0.24)
PositionEntity(PlayerEllipsoid,50,0.24+0.1,50)
PlayerLife = 1.0
PlayerPrevMS = MilliSecs()
NameEntity(PlayerShape,"PLA"+Str(1))

End Function

Function UpdatePlayerBC()

;turn left/right
MoveMouse(GraphicsWidth()/2,GraphicsHeight()/2)
PlayerYaw = PlayerYaw - Float(MXDiff)/10
RotateEntity(PlayerEllipsoid,0,PlayerYaw,0,False)
;move forward/backward/left/right
If( KeyDown(17)=0 And KeyDown(31)=0 And KeyDown(30)=0 And KeyDown(32)=0 )
  MoveEntity(PlayerEllipsoid,0,0,0)
Else If( KeyDown(17)=1 )
  MoveEntity(PlayerEllipsoid,0,0,+0.347)
Else If( KeyDown(31)=1 )
  MoveEntity(PlayerEllipsoid,0,0,-0.347)
Else If( KeyDown(30)=1 ) 
  MoveEntity(PlayerEllipsoid,-0.347,0,0)
Else If( KeyDown(32)=1 )
  MoveEntity(PlayerEllipsoid,+0.347,0,0)
EndIf

;shoot a bullet
If( MouseHit(1)=1 )
  NowMS% = MilliSecs()
  ;allow to shoot a new bullet only if the previous bullet was shot more than 100ms ago (1000milliseconds = 1second)
  If( NowMS - PlayerPrevMS > 100 )
   ;create a new bullet
   BulletsCount = BulletsCount + 1
   bu.bullet = New bullet
   bu\Shape = CopyEntity(XBullet)
   bu\Ellipsoid = CreatePivot()
   PositionEntity(bu\Ellipsoid,EntityX(PlayerShape,True),EntityY(PlayerShape,True)+0.875,EntityZ(PlayerShape,True))
   RotateEntity(bu\Ellipsoid,EntityPitch(PlayerShape,True),EntityYaw(PlayerShape,True),EntityRoll(PlayerShape,True))
   MoveEntity(bu\Ellipsoid,0,0,+0.25+0.1+0.01)
   TurnEntity(bu\Ellipsoid,Rand(-1,+1),Rand(-1,+1),0)
   bu\Life# = 1.0
   EntityRadius(bu\Ellipsoid,0.1)
   EntityType(bu\Shape,GShape) : EntityType(bu\Ellipsoid,GTurningMoving)
   PlayerPrevMS = NowMS
  EndIf
EndIf

End Function

Function UpdateBotsBC()

For bo.Bot = Each Bot

  ;depending on ai (wander, pursue and attack, flee and hide), decide how bot will move / act

Next

End Function

Function UpdateBulletsBC()

For bu.Bullet = Each Bullet

  ;decrease bullet life so that it does not last forever
  bu\Life = bu\Life - 0.01
  ;if life is more than 0, update the bullet
  If( bu\Life > 0 )
   ;move the bullet
   MoveEntity(bu\Ellipsoid,0,0,+1.2)

  ;if life is equal or less than 0, destroy the bullet
  Else If( bu\Life <= 0 )
   FreeEntity(bu\Shape) : Delete(bu)
   BulletsCount = BulletsCount - 1
  EndIf

Next

End Function

Function UpdatePlayerAC()

;position rotate PlayerShape depending on PlayerEllipsoid position orientation (-0.25 on the Y axis because of the ellipsoid radius)
PositionEntity(PlayerShape,EntityX(PlayerEllipsoid,True),EntityY(PlayerEllipsoid,True)-0.25,EntityZ(PlayerEllipsoid,True))
RotateEntity(PlayerShape,EntityPitch(PlayerEllipsoid,True),EntityYaw(PlayerEllipsoid,True),EntityRoll(PlayerEllipsoid,True))

;retrieve infos about the collisions which happened between Player ellipsoid and others collidables
;count the number of collisions which happened to Player ellipsoid
CC% = CountCollisions(PlayerEllipsoid)
;if there was at least one collision (i only consider the first)
If( CC > 0 )
  EntRef% = CollisionEntity(PlayerEllipsoid,1) : EntName$ = EntityName(EntRef) : EntKind$ = Left(EntName,3) : EntH% = Right(EntName,Len(EntName)-3)
  CollX# = CollisionX(PlayerEllipsoid,1) : CollY# = CollisionY(PlayerEllipsoid,1) : CollZ# = CollisionZ(PlayerEllipsoid,1)
  CollNX# = CollisionNX(PlayerEllipsoid,1) : CollNY# = CollisionNY(PlayerEllipsoid,1) : CollNZ# = CollisionNZ(PlayerEllipsoid,1)
  ;debug collision point and collision normal
  PositionEntity(CollPoint,CollX,CollY,CollZ)
  PositionEntity(CollNormal,CollX,CollY,CollZ) : AlignToVector(CollNormal,CollNX,CollNY,CollNZ,3,1.0)

  ;update Player consequently

EndIf

End Function

Function UpdateBotsAC()

For bo.Bot = Each Bot

  ;position rotate bot Shape depending on bot ellipsoid position orientation (-0.25 on the Y axis because of the ellipsoid radius)
  PositionEntity(bo\Shape,EntityX(bo\Ellipsoid,True),EntityY(bo\Ellipsoid,True)-0.25,EntityZ(bo\Ellipsoid,True))
  RotateEntity(bo\Shape,EntityPitch(bo\Ellipsoid,True),EntityYaw(bo\Ellipsoid,True),EntityRoll(bo\Ellipsoid,True))

  ;retrieve infos about the collisions which happened between bot ellipsoid and others collidables
  ;count the number of collisions which happened to bot ellipsoid
  ;CC% = CountCollisions(bo\Ellipsoid)
  ;if there was at least one collision (i only consider the first)
  ;If( CC > 0 )
   ;EntRef% = CollisionEntity(bo\Ellipsoid,1) : EntName$ = EntityName(EntRef) : EntKind$ = Left(EntName,3) : EntH% = Right(EntName,Len(EntName)-3)
   ;CollX# = CollisionX(bo\Ellipsoid,1) : CollY# = CollisionY(bo\Ellipsoid,1) : CollZ# = CollisionZ(bo\Ellipsoid,1)
   ;CollNX# = CollisionNX(bo\Ellipsoid,1) : CollNY# = CollisionNY(bo\Ellipsoid,1) : CollNZ# = CollisionNZ(bo\Ellipsoid,1)
   ;update Bot consequently
  ;EndIf

Next

End Function

Function UpdateBulletsAC()

For bu.Bullet = Each Bullet

  ;position rotate bullet Shape depending on bullet ellipsoid position orientation
  PositionEntity(bu\Shape,EntityX(bu\Ellipsoid,True),EntityY(bu\Ellipsoid,True)-0.25,EntityZ(bu\Ellipsoid,True))
  RotateEntity(bu\Shape,EntityPitch(bu\Ellipsoid,True),EntityYaw(bu\Ellipsoid,True),EntityRoll(bu\Ellipsoid,True))

  ;retrieve infos about the collisions which happened between Bullet ellipsoid and others collidables
  ;count the number of collisions which happened to Bullet ellipsoid
  CC% = CountCollisions(bu\Ellipsoid)
  ;if there was at least one collision (i only consider the first)
  If( CC > 0 )
   EntRef% = CollisionEntity(bu\Ellipsoid,1) : EntName$ = EntityName(EntRef) : EntKind$ = Left(EntName,3) : EntH% = Right(EntName,Len(EntName)-3)
   CollX# = CollisionX(bu\Ellipsoid,1) : CollY# = CollisionY(bu\Ellipsoid,1) : CollZ# = CollisionZ(bu\Ellipsoid,1)
   CollNX# = CollisionNX(bu\Ellipsoid,1) : CollNY# = CollisionNY(bu\Ellipsoid,1) : CollNZ# = CollisionNZ(bu\Ellipsoid,1)
   ;update bot consequently
   ;destroy the bullet
   FreeEntity(bu\Shape) : FreeEntity(bu\Ellipsoid) : Delete(bu)
   BulletsCount = BulletsCount - 1
   ;get kind of the picked entity
   EntName$ = EntityName(EntRef) : EntKind$ = Left(EntName,3) : EntH% = Right(EntName,Len(EntName)-3)
   If( EntKind = "GRO" )
    MaterialR% = 050 : MaterialG% = 150 : MaterialB% = 050
   Else If( EntKind = "ROC" )
    MaterialR% = 125 : MaterialG% = 125 : MaterialB% = 125
   Else If( EntKind = "TRE" )
    MaterialR% = 100 : MaterialG% = 050 : MaterialB% = 000
   Else If( EntKind = "BOT" )
    MaterialR% = 125 : MaterialG% = 125 : MaterialB% = 255
   Else If( EntKind = "PLA" )
    MaterialR% = 000 : MaterialG% = 000 : MaterialB% = 255
   EndIf
   ;create some particles
   For n% = 1 To 16 Step 1
    pa.Particle = New Particle : ParticlesCount = ParticlesCount + 1
    pa\Shape = CopyEntity(XParticle)
    EntityColor(pa\Shape,MaterialR,MaterialG,MaterialB)
    PositionEntity(pa\Shape,CollX,CollY,CollZ,True)
    AlignToVector(pa\Shape,CollNX,CollNY,CollNZ,3,1.0) : TurnEntity(pa\Shape,Rand(-45,+45),Rand(-45,+45),0)
    pa\Life# = 1.0
   Next
  EndIf

Next

End Function

Function UpdateParticles()

For pa.Particle = Each Particle
  MoveEntity(pa\Shape,0,0,0.03)
  pa\Life = pa\Life - 0.03
  EntityAlpha(pa\Shape,pa\Life)
  If( pa\Life <= 0 )
   FreeEntity(pa\Shape) : Delete(pa)
   ParticlesCount = ParticlesCount - 1
  EndIf
Next

End Function