slow motion / bullet time concepts

Started by RemiD, April 06, 2023, 11:05:57

Previous topic - Next topic

RemiD

#15
i have translated the code example by Midimaster, into bb :

Graphics( 800, 600, 32, 2 )

Global SloMo% = 1
Global X#, Y#
Global Time%

Time = MyMilliSecs()

Repeat
   
 If( KeyHit(57)=1 )
  If( SloMo = 1 )
  SloMo = 4       
  Else If( SloMo = 4 )
  SloMo = 1
  EndIf
  Time = MyMilliSecs()
 EndIf
 
 If( Time < MyMilliSecs() )
  Time = MyMilliSecs()+1
  Y = 600/2-10/2
  X = X +1 : If( X > 800-1 ) : X = 0 : EndIf
 EndIf   

 SetBuffer(BackBuffer())
 ClsColor(000,000,000) : Cls()

 Color( 240, 240, 240 ) : Rect( X, Y, 10, 10, 1 )   

 Color( 240, 240, 240 ) : Text( 0, 0, "/"+SloMo )

 Flip(0)

Until( KeyDown(1)=1 )
 
Function MyMillisecs%()
    Return MilliSecs() / SloMo
End Function
 


@Midimaster >> i hope this is what you wanted to demonstrate ?

so if i understand correctly, the idea is to update the transforms (moves / turns / positions / rotations) only if enough millis have elapsed, but the slomo value can slow down when the next update is done, correct ?

Derron

if you did it that way "slow motion" would look "non smooth". 

In slow-motion you just move in "more little" steps.
If you only updated less often, then during rendering you need to "interpolate" between "last pos" and "next pos".

Think of a "flip-book" - if you flip "slower", the animation will become ... less smooth. You need more drawings (intermediate animation poses) instead of flipping less often. And so the same for physics: you need "more steps" (albeit they are "smaller" then).



bye
Ron

Midimaster

As long as the refreshing of the values is more often than 60Hz you cannot see any "non smooth" effect.

In his example he calls the regular timing 1000 times per second and in slow motion "only" 500 times with a factor of SloMo-factor of 2.

This system is much easyer to implement in an given code, which was based on millisecs, than new inplement a complete delta timing system.

You only have to care about motions which you call less often than every 16msec, 16 corresponds to Screen FLIP 60Hz.

CASES:

When you move clouds  on the sky every 100msec for 1 pixel... In SloMo they would move every 200msec for 1 pixel. That's still ok because the visible minimum on the screen is 1 pixel.

When you moved  a car for 6 pixel every 18msec... In SloMo it would now move every 36msec for 6 pixel. This is a pitty. You should change the code for both RealTime and SlowMo: Move the car 1 pixel every 3msec, then you become 1 pixel every 6msec in SlowMo.

You could write a main loop, that branches into drawing only when 16msec have passed:

Code (vb) Select
Global DrawTimer:Int
Repeat
    If DrawTimer<Millisecs()
          DrawTimer = Millisecs()+16
          Cls
          DrawAll....
          Flip 0
    Else
          CalculateAll
          Delay 1
    Endif
Until AppTerminate()


Function CalculateAll()
    If BallTimer<MyMillsecs()
          BallTimer = MyMillsecs()+5
          MoveBall()
    endif
    If PlayerTimer<MyMillsecs()
          PlayerTimer = MyMillsecs()+10
          MovePlayer()
    Endif
    If CloudTimer<MyMillsecs()
          CloudTimer = MyMillsecs()+100
          MoveClouds()
    Endif
    .....
End Function

So you will get 1000 main loops per second.

...back from Egypt

Derron

You should have physics steps "shared" across the objects. And not "every x milliseconds for car 1, every y milliseconds for body 5" (else you will need to split "collision checks" etc - so that they all check "at the same time" regarding physics/world simulation).

Also you sooner or later have fast moving objects which in slow mo would be "visible" if the steps "between updates" (or renders when interpolating/tweening) are too big.

60 Hz: people expect you to support 144hz today (and more).
Do not make your logic dependent on some assumed refresh rates (it looks "sluggish" if someone has 75hz or 30hz or ...) - make your logic independent from it. You get a delta time (time between "frames") and this is your factor for movement.
Adjust the factor and things move slower or faster "from frame to next frame".

You can limit how often physics update (1000 times a second -- or even only 30) but of course each update comes with CPU cycle cost. People proposed high physics update rates to avoid bullets not hitting a thin wall etc.
If your hardware is capable of simulating the physics 1000 times a second (plus rendering it eg 144 times a second) then you might come away by updating "less often" / skipping updates ... but it _will_ stutter visually here and there (depends on how the update cycles and the render calls are scheduled).


I think you should check how bullet time is done in other engines/languages and simply choose what they did.


bye
Ron



RemiD

#19
@Derron>>
QuoteI think you should check how bullet time is done in other engines/languages and simply choose what they did.
it is not that easy because a lot of things are done behind the scene for Unity engine and Unreal engine, whereas for Blitz3d you have to code more low level stuff.


QuoteAs long as the refreshing of the values is more often than 60Hz you cannot see any "non smooth" effect.
i think that with more than 15fps, even if each step of turn / move / animation frames are not really ideal, it would not be noticeable.


@Midimaster>>
i am going to implement your method with the same scene that i made (using delay for the slow motion effect) and see if it works well...
if my understanding of your method is correct, it should work...

Derron

#20
https://gamedev.stackexchange.com/questions/37976/how-to-code-time-stop-or-bullet-time-in-a-game
-> use deltatime and a modifier


I still do not understand why you have issues using this simple "modified deltatime" approach. Any physics are based on ...time. So if you adjust time, then physics just happens "slower".
If a character jumps then it has energy for the upwards movement and there is gravity which tries to "move it down" (with around 9,81m/s²). so the downwards acceleration is based on time too (t²).

The "time" grows by "deltatime" (or here: "Time = Time + deltatime * globalspeedModifier"). Each time you "update()" your entities (the jumping character) you do this by "jumpingCharacter.update(deltatime * globalspeedModifier)".
Inside that method you update the position of the element based on "how would it move per second" - but multiply this value with "deltatime" _and_ your individual slowmo-modifier (or speedup).
This way the same movement (moving from x10 to x100 - or jumping up and falling down) just takes "longer".

But the important thing is: the jump "curve" will be the same as with normal time - it just takes longer, or less long than "normal".



Using the deltatime approach even allows to use one of these "fancy" interpolation/easing formulas (start slow, become fast, finish slow ...)


bye
Ron

Derron

#21
The following code has 2 balls with the same initial velocity ("movement").
When I hit "space" they jump, when I hit "b" then for ball1 the time will pass at 50% speed (so deltatime * 0.5).
when jumping the jump for the "bullet time" ball will reach the same height, the same distance .. it just takes longer.
 
SuperStrict
Framework Brl.StandardIO
Import Brl.GLMax2D
Import "../source/Dig/base.util.deltatimer.bmx"

Graphics 800, 600
Global dt:TDeltatimer = New TDeltaTimer.Init(100, -1)
dt._funcUpdate = Update
dt._funcRender = Render

Global ball1:TBall = New TBall(0, 200)
Global ball2:TBall = New TBall(0, 400)

Repeat
dt.Loop()
Until KeyHit(KEY_ESCAPE) or AppTerminate()

'-----

Function Update:Int()
'logic
If KeyHit(KEY_SPACE)
ball1.Jump()
ball2.Jump()
EndIf
If KeyHit(KEY_B)
if ball1.timeMod = 1.0
ball1.timeMod = 0.5
Else
ball1.timeMod = 1.0
EndIf
EndIf
ball1.Update(dt.GetDelta())
ball2.Update(dt.GetDelta())
End Function


Function Render:Int()
'render
Cls
ball1.Render()
ball2.Render()
Flip 0
End Function



Type TBall
Field x:Float, y:Float
Field vx:Float, vy:Float
Field groundY:Float
Field size:Int
Field timeMod:Float = 1.0
Global jumpStrength:int = 300
Global gravity:Float = 350

Method New(x:Float, y:Float)
self.x = x
self.y = y
self.vx = 200 'initial horizontal speed in px/second
self.size = 50
self.groundY = y 'don't go lower than this
End Method


Method Jump()
'jumping means adjusting your velocity ("upwards = neg. y")
'only jump if not falling/jumping
if vy = 0
vy :- jumpStrength
EndIf
End Method


Method Update(dt:Float)
Local effDT:Float = dt * timeMod
'adjust position
x :+ vx * effDT
y :+ vy * effDT

'adjust velocity
'turn at left or right
if x > 800 or x < 0 then vx = -1 * vx
'incorporate jumping
vy :+ gravity * effDT

'do not go below ground
if y >= self.groundY
y = self.groundY
vy = 0
EndIf
End Method


Method Render()
DrawOval(x - size*0.5, y - size, size*0.5, size*0.5)
End Method
End Type


Attaching a little GIF animation (recorded with 15 fps ... so less smooth than in real time).
Peek 2023-04-26 21-23.gif

The DeltaTimer is from my DIG-framework but you can of course use your own stuff - just wanted to keep the code short:
https://github.com/TVTower/TVTower/blob/master/source/Dig/base.util.deltatimer.bmx


bye
Ron

RemiD

#22
@Derron >> thanks, i will take a look.


edit : i can't convert your code to bb because some parts are missing.

but whatever, your deltacoef * speedcoef (modifier) looks like what i have already tried (what is explained in the gamedev article) and it seems to work for simple turns / moves, but it somehow decreases the amplitudes of the turns / movements when the 'physics' are more complex.


back to using delay for the moment, but i will do more experiments later... 

Derron

Please show me the "more complex" physics.

As said ... there is one thing in physics we can almost rely on - and this is "time". As long as you do not go near or past the speed of light you can use it for many things.
Means if something goes "wonky" if it's time "advances slower" then the algorithms of that physic whatever are most probably bugged/incorrect.

Think of "impact" - the force of eg a bullet hitting a body (to calculate how far the body will be moved backwards). the force is the same as the mass is the same and the speed is the same (if bullet and body have the same "speed modifier").
And this is now the interesting part: if the bullet moves at "normal speed" and that body in "bullet time" (eg half time speed) it means it will hit the body with "double the speed" (relatively spoken) and thus the applied force will automatically be higher for the body (and thus for the bullet on impact too).


As said - it all should work as "time" is what you can use as base - and time is what you kinda "adjust".


I hope you find the time to toy around with it - you better trust what many articles suggest you to do (they cannot all be wrong here).


bye
Ron

RemiD

@Derron >> yes thanks for the code example :) , i will have to spend more time studying it.
but not my priority right now.

thanks

Midimaster

#25
I updated your code from post#15 to a more precise version.

Using the Millisecs() for calculation is dangerous

The Millisecs() timer has two problems.
1.
Adding values to it returns unexpected inaccurate time steps.

2.
Calling mor often than 500 times a second is to inacurate.

I tested this:

to use...
If Time<Millisecs()
    Time = Millisecs() + 1

...does not call this IF-branch 1000 times a second as expected, but only ~450 times.  More than 55% deviation!!!!

And also for a bigger time distance...
If Time<Millisecs()
    Time = Millisecs() + 2

...does not call this IF-branch 500 times a second as expected, but only ~300 times. More than 33% deviation!!!!



You better add the time distance to Time%

If Time<Millisecs()
    Time = Time + 1
...calls the IF-branch ~950 times a second. Only 5% deviation!!!! 

and for a bigger time distance...
If Time<Millisecs()
    Time = Time + 2
...calls the timer 499-501 times a second. Only 0.2% deviation!!!! 


The same results with MyMillisecs()


So we can note, that
1.
...usings the Millisecs()-timer for calculating the next time step is not adwised!!

2.
...calling the Millisecs() with a time distance of 0 or 1 makes to big deviations. But using time distances "2" and more brings high accuracy.


Switching to SlowMo and back

If you handle a lot of object with the MyMillisecs() function you have to care about the moments when switching from one speed to another. It can happen that some actors stand still for a while. because of (old) unvalid timestamps.

To prevent this I expanded the MyMillisecs() to handle all Motion-Speed-Changes, so that the user needs not to care about it. This shortens (and simpyfies) the code for moving the actors


so your BB-example would now look like this:


Graphics( 800, 600, 32, 2 )
; new line:
Global LastFakeTime%, LastRealtime%, LastSloMo#, FakeTime%
Global SloMo# = 1
....
Repeat
     ...
    If( Time < MyMilliSecs() )
        ; new line:
        Time = Time+2
        Y = 600/2-10/2
        X = X +1 : If( X > 800-1 ) : X = 0 : EndIf
    EndIf
    ....
Until....

;new function
Function MyMillisecs%()
    If LastSloMo=0 Then  FakeTime = MilliSecs()

    If (SloMo <> LastSloMo)
        LastSloMo    = SloMo
        LastRealTime = MilliSecs()
        LastFakeTime = FakeTime
    EndIf

     FakeTime = LastFaketime + (MilliSecs() - LastRealTime) / SloMo
    Return FakeTime
End Function   



This would now work with any number of actors in the game:


Here a example with 2 actors: a ball and a jumping rectangle. Individual timing for each actor

Graphics( 800, 600, 32, 2 )
 
Global SloMo# = 1
Global X#, Y#, BallX#

Global LastFakeTime%, LastRealtime%, LastSloMo#, FakeTime%

Global RectTime% = MyMilliSecs()
Global BallTime% = MyMilliSecs()

Repeat  
     If( KeyHit(57)=1 )    ;SPACE
          If( SloMo = 1 )
              SloMo = 4       
          Else If( SloMo = 4 )
              SloMo = 1
          EndIf
     EndIf

    MoveTheRect
 
    MoveTheBall

    SetBuffer(BackBuffer())
    ClsColor(000,000,000)

    Cls()
    
    Rect( X, Y, 10, 10, 1 )   
    Oval( BallX, 400, 10, 10, 1 )   
    Text( 0, 0, "/"+SloMo )   
    Flip 0
Until KeyDown(1)


Function MoveTheRect()
    If RectTime >= MyMilliSecs() Then Return 

    RectTime = RectTime+2
    ; calculations of the object
    Y = 300 -Sin(x)*100
    X = X +1   
    If( X > 800-1 ) Then X = 0    
End Function


Function MoveTheBall()
    If BallTime >= MyMilliSecs() Then Return 

    BallTime = BallTime+5
    BallX = BallX +1
    If( BallX > 800-1 ) Then BallX = 0
End Function

 
Function MyMillisecs%()
    If LastSloMo=0 Then  FakeTime = MilliSecs()

    If (SloMo <> LastSloMo)
        LastSloMo    = SloMo
        LastRealTime = MilliSecs()
        LastFakeTime = FakeTime
    EndIf

     FakeTime = LastFaketime + (MilliSecs() - LastRealTime) / SloMo
    Return FakeTime
End Function
...back from Egypt

Derron

QuoteYou better add the time distance to Time%

If Time<Millisecs()
    Time = Time + 1
...calls the IF-branch ~950 times a second. Only 5% deviation!!!!


Now think about WHAT it actually does ...
Now think about WHEN it actually does these things.


What your code does is - it "catches up". So if there is a stutter after 500 ms for 300 ms ... then within these 300 ms you do not "update", timer will be 300ms "below" millisecs().

If now the update cycle happens more than 1 time per millisecond it will "catch" up (run the code and increase "time + 1").
If the update cycle happens less than 1 time a millisecond (so maybe takes 10 ms for some reason) it won't catch up ... it will still be behind the current time.

As update cycles are not guaranteed to require the same time these "time changes" will happen "Non-smooth".


And thus makes an animation based on this "time" value ... run ... "wobbly", "unsteady" or simply non-smooth.


It is much easier to track time since "last call" and calculate a fraction of a second which has passed - and do your physics/animation based on this.
Saves you from hoping to have 1000 update calls distributed along the millisecond slots accurately - this won't happen.
Also calculating all these things for 1000 times a second will require some ... cpu ticks/time.
Not to talk about potential hickups when a Garbage Collector decides to do clean up (important).



@Midimaster
You might track "Millisecs()" of each cycle ... and at the end store it in a csv for later examination (or display "afterwards" so you do not influence what is done "during" your simulation).
This will show how "smooth" cycles happen - or not. Then you can also add "random" delay(1-5ms) to simulate "cpu load" or so. Maybe it exposes a flaw (or maybe it shows that it works nicely).


bye
Ron

Midimaster

#27
Here is your code from post#13 with a realistic physic and use of a MyMilliTimer()

SlowMotion.gif


Code (vb) Select
;slow motion effect using MyMillisecs() by RemiD ( 20230413 )
;while on ground, press space to jump
;while on air, hold altleft to slow down time
 
Graphics3D( 640,480,32,2 )
 
Global Camera = CreateCamera()
CameraRange( Camera, 0.1, 100 )
CameraClsColor( Camera, 000, 000, 000 )
PositionEntity( Camera, +10, 5, 25 )

Global Light = CreateLight(1)
LightRange Light,1000
PositionEntity Light, 10,50,20

 
;ground
Global ground_mesh
ground_mesh = CreateCube() : ScaleMesh( ground_mesh, 300.0/2, 0.1/2, 300.0/2) : PositionMesh( ground_mesh, 0, -0.1/2, 0 )
EntityColor  ground_mesh, 0, 55, 0
 
;thing
Global thing_movespeed#
Global thing_jumpspeed#
Global thing_flipspeed#
Global GravitySpeed# = 1

Const GAME_FACTOR# =0.3

;**** SLOW MOTION TIMER ************************************
Global LastFakeTime%, LastRealtime%, LastSloMo#, FakeTime%, FlipTime%
Global SloMo#=1

 
Global thing_root = CreatePivot()
Global thing_mesh = CreateCubicMan()
EntityColor( thing_mesh, 255, 222, 222)
MoveEntity( thing_mesh, 0, +0.875, 0 )
EntityParent( thing_mesh, thing_root, True )

SetBuffer( BackBuffer() )
Global ManTimer% = MilliSecs()

PointEntity camera, Thing_root
MoveEntity camera, 0,0,15

PointEntity light, thing_mesh

Repeat
MoveMan
If KeyDown(56)
SloMo=4
Else
SloMo=1
EndIf
RenderWorld()

Flip 0        
Until( KeyDown(1)=1 )


Function MoveMan()
If ManTimer>MyMilliSecs() Return

ManTimer = MyMilliSecs() +12 

If( EntityY(thing_root,True) < 0 )
  ;is on ground
  PositionEntity( thing_root, EntityX(thing_root,True), 0, EntityZ(thing_root,True) )
  thing_jumpspeed = 0
  GravitySpeed = 1

ElseIf( thing_jumpspeed = 0 )
  If( KeyHit(57)=1 )
;jump
   thing_movespeed =  0.1
   thing_jumpspeed =  0.20
   thing_flipspeed =  7.95
  EndIf
Else
;is on air
  thing_jumpspeed = thing_jumpspeed - (GravitySpeed-1) * GAME_FACTOR
  GravitySpeed  = GravitySpeed*1.002
  ;turn & move
  TurnEntity( thing_mesh, thing_flipspeed , 0, 0 )
  MoveEntity( thing_root, 0, thing_jumpspeed, thing_movespeed)
EndIf
End Function

 
Function MyMillisecs%()
If LastSloMo=0 Then  FakeTime = MilliSecs()

If (SloMo <> LastSloMo)
LastSloMo    = SloMo
LastRealTime = MilliSecs()
LastFakeTime = FakeTime
EndIf

  FakeTime = LastFaketime + (MilliSecs() - LastRealTime) / SloMo
    Return FakeTime
End Function


Function CreateCubicMan()
mesh = CreateMesh()

head = CreateSphere(32)
ScaleMesh    head, 0.25, 0.30, 0.25
PositionMesh head, 0, 0.92, 0
EntityColor  head, 255,155,111
EntityParent head, Mesh


chest = CreateCube()
ScaleMesh    chest, 0.24, 0.3, 0.1
PositionMesh chest, 0, 0.27, 0
EntityColor  chest, 255,0,111
EntityParent chest, Mesh


arms = CreateCube()
ScaleMesh    arms, 0.85, 0.08, 0.08
PositionMesh arms, 0, 0.6, 0
EntityColor  arms, 255,0,111
EntityParent arms, Mesh

hips = CreateCube()
ScaleMesh    hips, 0.26, 0.15, 0.11
PositionMesh hips, 0, 0, 0
EntityColor  hips, 255,255,0
EntityParent hips, Mesh


leg1 = CreateCylinder()
ScaleMesh    leg1, 0.08, 0.4, 0.08
PositionMesh leg1, -0.15, -0.4, 0
EntityColor  leg1, 255,155,111
EntityParent leg1, Mesh

leg2 = CreateCylinder()
ScaleMesh    leg2, 0.08, 0.4, 0.08
PositionMesh leg2, +0.15, -0.4, 0
EntityColor  leg2, 255,155,111
EntityParent leg2, Mesh

Return mesh
End Function
...back from Egypt

RemiD

@Midimaster >> i will take a look at your code later, thanks

( maybe edit the description of the code with your name and date so that future viewers are not confused... )

Midimaster

Ah... following your PM I understand thst the actor should not follow a gravity, but a special (unrealistic) movement. so I adjusted the values of my example to get closer to your inital movement
now the values are:

GAME_FACTOR# =0.003
ManTimer = MyMilliSecs() +4
thing_movespeed =  0.03
thing_jumpspeed =  0.07
thing_flipspeed =  -1.4
GravitySpeed  = GravitySpeed*1.0005

RemiD.gif


This is the new version:
;; RemiD's code from post#13 modified by midimaster
;while on ground, press <SPACE> to jump
;while on air, hold <ALT LEFT> + <SPACE> to slow down time
 
Graphics3D( 640,480,32,2 )
 
Global Camera = CreateCamera()
CameraRange( Camera, 0.1, 100 )
CameraClsColor( Camera, 000, 000, 000 )
PositionEntity( Camera, 25, 5, 35 )

Global Light = CreateLight(1)
LightRange Light,1000
PositionEntity Light, 10,50,20

 
;ground
Global ground_mesh
ground_mesh = CreateCube() : ScaleMesh( ground_mesh, 300.0/2, 0.1/2, 300.0/2) : PositionMesh( ground_mesh, 0, -0.1/2, 0 )
EntityColor  ground_mesh, 0, 55, 0

Global GroundPivot=CreatePivot()
PositionEntity GroundPivot, 0,5,0
;thing
Global thing_movespeed#
Global thing_jumpspeed#
Global thing_flipspeed#
Global GravitySpeed# = 1

Const GAME_FACTOR# =0.003


;**** SLOW MOTION TIMER ************************************
Global LastFakeTime%, LastRealtime%, LastSloMo#, FakeTime%, FlipTime%
Global SloMo#=1

 
Global thing_root = CreatePivot()
Global thing_mesh = CreateCubicMan()
EntityColor( thing_mesh, 255, 222, 222)
MoveEntity( thing_mesh, 0, +0.875, 0 )
EntityParent( thing_mesh, thing_root, True )

SetBuffer( BackBuffer() )
Global ManTimer% = MilliSecs()

PointEntity camera, Thing_root
MoveEntity camera, 0,0,15

PointEntity light, thing_mesh
 
For i%=0 To 30
 c= CreateCylinder()
 ScaleEntity c, 0.2,10,0.2
  PositionEntity c, -10,-2,-100 + i*10

Next

;WireFrame True
Repeat
 
 MoveMan
PointEntity camera, GroundPivot
 
 If KeyDown(56)
 SloMo=4
 Else
 SloMo=1
 EndIf
 RenderWorld()


 Flip 0      
Until( KeyDown(1)=1 )



Function MoveMan()
 If ManTimer>MyMilliSecs() Return

 ManTimer = MyMilliSecs() +4


 If( EntityY(thing_root,True) < 0 )
   ;is on ground
   PositionEntity( thing_root, EntityX(thing_root,True), 0, EntityZ(thing_root,True) )
   thing_jumpspeed = 0
   GravitySpeed = 1
 
 ElseIf( thing_jumpspeed = 0 )
   If( KeyHit(57)=1 )
 ;jump
   thing_movespeed =  0.03
   thing_jumpspeed =  0.07
   thing_flipspeed =  -1.4
 EndIf
 Else
 ;is on air
   thing_jumpspeed = thing_jumpspeed - (GravitySpeed-1) * GAME_FACTOR
   GravitySpeed  = GravitySpeed*1.0005
   ;turn & move
   TurnEntity( thing_mesh, thing_flipspeed , 0, 0 )
   MoveEntity( thing_root, 0, thing_jumpspeed, thing_movespeed)
   MoveEntity GroundPivot, 0, 0, thing_movespeed
 EndIf
End Function


 
Function MyMillisecs%()
 If LastSloMo=0 Then  FakeTime = MilliSecs()

 If (SloMo <> LastSloMo)
 LastSloMo    = SloMo
 LastRealTime = MilliSecs()
 LastFakeTime = FakeTime
 EndIf

 FakeTime = LastFaketime + (MilliSecs() - LastRealTime) / SloMo
    Return FakeTime
End Function


 
Function CreateCubicMan()
 mesh = CreateMesh()

 head = CreateSphere(32)
 ScaleMesh    head, 0.25, 0.30, 0.25
 PositionMesh head, 0, 0.92, 0
 EntityColor  head, 255,155,111
 EntityParent head, Mesh


 chest = CreateCube()
 ScaleMesh    chest, 0.24, 0.3, 0.1
 PositionMesh chest, 0, 0.27, 0
 EntityColor  chest, 255,0,111
 EntityParent chest, Mesh


 arms = CreateCube()
 ScaleMesh    arms, 0.85, 0.08, 0.08
 PositionMesh arms, 0, 0.6, 0
 EntityColor  arms, 255,0,111
 EntityParent arms, Mesh

 hips = CreateCube()
 ScaleMesh    hips, 0.26, 0.15, 0.11
 PositionMesh hips, 0, 0, 0
 EntityColor  hips, 255,255,0
 EntityParent hips, Mesh


 leg1 = CreateCylinder()
 ScaleMesh    leg1, 0.08, 0.4, 0.08
 PositionMesh leg1, -0.15, -0.4, 0
 EntityColor  leg1, 255,155,111
 EntityParent leg1, Mesh

 leg2 = CreateCylinder()
 ScaleMesh    leg2, 0.08, 0.4, 0.08
 PositionMesh leg2, +0.15, -0.4, 0
 EntityColor  leg2, 255,155,111
 EntityParent leg2, Mesh

 Return mesh
End Function
...back from Egypt