Game Clock Code

Post your FreeBASIC source, examples, tips and tricks here. Please don’t post code without including an explanation.
Post Reply
Eclipzer
Posts: 432
Joined: Oct 01, 2005 10:50
Location: Maryland
Contact:

Game Clock Code

Post by Eclipzer »

I've updated my Game Clock routines for FBv0.18.3b. These routines can be used to easily add a game clock into your programs. The code support clock creation, start, stop, pause, reset. You can also add a set amount of time to an active clock as well.

Code: Select all

' Program:  Game Clock Routines
' Language: FreeBASIC v0.18.3b
' Author:   Quinton Roberts (Eclipzer)
' Date:     11-13-06
' Update:   02-24-08
'
' Copyright (c) 2006-2008.

' video page constants
  const FX_VIS_PAGE=0
  const FX_WRK_PAGE=1
  const FX_CLR_PAGE=2  
  
' screen mode constants
  const FX_320x240=14
  const FX_640x480=18
  const FX_800x600=19
  const FX_FULLSCREEN=1

' Data Structures  
  type clockdat        
    real  as integer 'timer
    init  as integer 'base time
    diff  as integer 'difference between init time and base time
    start as integer 'start time value (user defined)
    curr  as integer 'current time
    pause as integer 'time switch 0=on 1=off    
  end type
  
' Declarations
  declare sub io_clock_add    (t as clockdat,ss as integer=0,mm as integer=0,hh as integer=0)
  declare sub io_clock_pause  (t as clockdat)  
  declare sub io_clock_set    (t as clockdat,ss as integer=0,mm as integer=0,hh as integer=0)
  declare sub io_clock_start  (t as clockdat,ss as integer=-1,mm as integer=0,hh as integer=0)
  declare sub io_clock_stop   (t as clockdat)
  declare sub io_clock_update (t as clockdat)
  
  declare function io_time_hms(secs as integer) as string
    
  dim clock as clockdat
  
  Screen    FX_640x480,32,3,0
  ScreenSet FX_WRK_PAGE,FX_VIS_PAGE
  
  io_clock_start(clock,0) 'init game clock at 0
  
  dim as string  kb,state,divider
  dim as integer done,xx,yy  
  
  divider=" -----------------------------[ ||| eclipzer ||| ]-----------------------------"
  
  do
    
    io_clock_update(clock) 'update game clock each cycle
    
    kb=inkey$
    
    select case kb
    case chr$(27): done=1
    case "+":      io_clock_add(clock,15)
    case "-":      io_clock_add(clock,-15)
    case " ":      io_clock_pause(clock)
    case "x","X":  io_clock_stop(clock)
    case chr$(13): io_clock_set(clock,0)
    case else
    end select
    
    if clock.pause then state="Paused" else state="Active"
    
    ScreenCopy FX_CLR_PAGE,FX_WRK_PAGE
    
    locate 1,1
    ?
    ? " Game Clock Routines"
    ? divider
    ?
    ? " +----------------------------+"
    ? " |                            |"
    ? " |                            |"
    ? " +----------------------------+"
    ?
    ? " Key Controls"
    ? "  + ........ Add 15 seconds to game clock"
    ? "  - ........ Subtract 15 seconds from game clock"
    ? "  SPACE .... Toggle game clock on/off (pause)"
    ? "  ENTER .... Reset game clock"
    ? "  X ........ Stop game clock"
    ? "  ESC ...... Exit"
    ?

    
    xx=4
    yy=6
    locate yy,xx: ? "Game Clock:  "; io_time_hms(clock.curr)
    locate   ,xx: ? "Clock State: "; state
    
    locate 27,1
    ? divider    
    ?
    ? " Copyright (c) Quinton Roberts 2006-2008."

    ScreenCopy
    
  loop until done
  
' =============================================================================
'          Name: io_clock_add (02.24.08)
'       Returns: 
'    Parameters:
'            ss: init seconds [0]
'            mm: init minutes [0]
'            hh: init hours [0]
'             t: time structure
' -----------------------------------------------------------------------------
'   Description: Add set amount of time to play clock.
'      Comments: 
' =============================================================================
  sub io_clock_add(t as clockdat,ss as integer,mm as integer,hh as integer)
    t.start+=hh*3600+mm*60+ss    
  end sub
  
' =============================================================================
'          Name: io_clock_pause (02.02.07)
'       Returns: 
'    Parameters:
'             t: time structure
' -----------------------------------------------------------------------------
'   Description: Toggle time structure for updating.
'      Comments: 
' =============================================================================
  sub io_clock_pause(t as clockdat)
    select case t.pause
    case 0:    t.pause=1: t.start=t.curr: t.diff=0 'pause time if active
    case else: io_clock_start(t)                   'start time if paused
    end select
  end sub

' =============================================================================
'          Name: io_clock_set (02.02.07)
'       Returns: 
'    Parameters:
'             t: time structure
'            ss: init seconds [0]
'            mm: init minutes [0]
'            hh: init hours [0]
' -----------------------------------------------------------------------------
'   Description: Set a specific time for a time structure.
'      Comments: Only the time is set. To actually start the clock, use the
'                io_clock_start routine.
' =============================================================================
  sub io_clock_set(t as clockdat,ss as integer,mm as integer,hh as integer)    
    t.init=timer             'get new init time
    t.start=hh*3600+mm*60+ss 'set time value    
  end sub

' =============================================================================
'          Name: io_clock_start (02.24.08)
'       Returns: 
'    Parameters:
'             t: time structure
'            ss: init seconds [-1]
'            mm: init minutes [0]
'            hh: init hours [0]
' -----------------------------------------------------------------------------
'   Description: Init time structure for updating.
'      Comments: You should call this routine at least once before using a new
'                clock structure. You can pass a starting clock time through the
'                ss,mm,hh parameters. If no value is passed for ss, then the
'                clock time will retain its current value.
' =============================================================================  
  sub io_clock_start(t as clockdat,ss as integer,mm as integer,hh as integer)
    t.init=timer                           'get new init time
    t.pause=0                              'allow time updates    
    if ss>=0 then t.start=hh*3600+mm*60+ss 'set new start time
  end sub

' =============================================================================
'          Name: io_clock_stop (02.02.07)
'       Returns: 
'    Parameters:
'             t: time structure
' -----------------------------------------------------------------------------
'   Description: Prevent time structure from updating.
'      Comments: 
' =============================================================================       
  sub io_clock_stop(t as clockdat)     
    t.diff=0       'clear time difference
    t.start=t.curr 'save current time    
    t.pause=1      'prevent time updates
  end sub  

' =============================================================================
'          Name: io_clock_update (02.02.07)
'       Returns: 
'    Parameters:
'             t: time structure
' -----------------------------------------------------------------------------
'   Description: Update current time structure.
'      Comments: This should be called once every program cycle. 
' =============================================================================
  sub io_clock_update(t as clockdat)
    t.real=timer
    if (t.pause=0) then t.diff=t.real-t.init 'update time difference
    t.curr=t.start+t.diff                    'update current time
  end sub

' =============================================================================
'          Name: io_time_hms (02.24.08)
'       Returns: time string
'    Parameters:
'          secs: seconds to convert
' -----------------------------------------------------------------------------
'   Description: Converts seconds to a string in HH:MM:SS format.
'      Comments: 
' =============================================================================     
  function io_time_hms(secs as integer) as string
    
    dim as integer hh, mm, ss
    dim as string  hhs,mms,sss
      
    ss=secs
    hh=ss\3600: ss-=3600*hh 
    mm=ss\60:   ss-=60*mm
      
    hhs=str$(hh): if len(hhs)<2 then hhs="0"+hhs
    mms=str$(mm): if len(mms)<2 then mms="0"+mms
    sss=str$(ss): if len(sss)<2 then sss="0"+sss
      
    return hhs+":"+mms+":"+sss
      
  end function
  
  
  
-Eclipzer
badmrbox
Posts: 664
Joined: Oct 27, 2005 14:40
Location: Sweden
Contact:

Post by badmrbox »

Sweet.
Eclipzer
Posts: 432
Joined: Oct 01, 2005 10:50
Location: Maryland
Contact:

Post by Eclipzer »

I've put some more work into these clock routines and have an updated version here. Three new added features are milli-second support and count down clocks and alarms. The demo code here shows 4 different clocks running simultaneously. The clocks start and end at various times, and can count down/forward independent of each other. I've also renamed and added variables associated with the clock structure.

Though everything is working, I'm still fleshing out the routines to make implementing them into your own programs much easier.

Code: Select all

' Program:  Game Clock Routines
' Version:  0.04
' Language: FreeBASIC v0.18.3b
' Author:   Quinton Roberts (Eclipzer)
' Date:     11-13-06
' Update:   03-25-08
'
' Copyright (c) 2006-2008.

' define arrow keys
  #define ASC_UP     chr$(255)+chr$(72)
  #define ASC_DOWN   chr$(255)+chr$(80)  
  #define ASC_LEFT   chr$(255)+chr$(75)  
  #define ASC_RIGHT  chr$(255)+chr$(77)

' video page constants
  const FX_VIS_PAGE=0
  const FX_WRK_PAGE=1
  const FX_CLR_PAGE=2  
  
' screen mode constants
  const FX_320x240=14
  const FX_640x480=18
  const FX_800x600=19
  const FX_FULLSCREEN=1
  
' clock constants
  const IO_FORWARD=1
  const IO_BACKWARD=0
  
' clock flags  
  const fUPDATE=1 '0=off 1=update
  const fCOUNT=2  '0=backward 1=forward
  const fALARM=3  '0=off 1=triggered  

' Data Structures  
  type clockdat    
    as integer flags  'clock flags (update,count,alarm)
    as double  real   'timer sample    
    as double  epoch  'initial time sample (base time)
    as double  lapse  'sampled difference between timer and epoch
    as double  start  'static start time value (user defined)
    as double  alarm  '       alarm time value (user defined)
    as double  value  'dynamic start time value
    as double  curr   'current time    
  end type
  
' Declarations  
  declare function clock_state(t as clockdat) as string
  
  declare function io_bit    (byref vdat as uinteger,idx as uinteger) as uinteger
  declare sub      io_bit_clr(byref vdat as uinteger,idx as uinteger)
  declare sub      io_bit_flp(byref vdat as uinteger,idx as uinteger)
  declare function io_bit_get(byref vdat as uinteger,idx as uinteger,bits as uinteger) as uinteger
  declare sub      io_bit_put(byref vdat as uinteger,idx as uinteger,bits as uinteger,value as uinteger)  
  declare sub      io_bit_set(byref vdat as uinteger,idx as uinteger)
  
  declare sub io_clock_inc    (t as clockdat,hh as double=0,mm as double=0,ss as double=0)
  declare sub io_clock_pause  (t as clockdat)
  declare sub io_clock_set    (t as clockdat,hh as double=0,mm as double=0,ss as double=0)
  declare sub io_clock_start  (t as clockdat,update as integer=1)
  declare sub io_clock_stop   (t as clockdat)
  declare sub io_clock_update (t as clockdat)
  
  declare function io_time_hms(tt as double) as string
  
  const CLOCKS=4
    
  dim shared clock(CLOCKS-1) as clockdat
    
  dim as string  kb,state,div1,div2,div3
  dim as integer i,done,xx,yy,idx=0
  
  Screen    FX_640x480,32,3,0
  ScreenSet FX_WRK_PAGE,FX_VIS_PAGE
  
  for i=0 to CLOCKS-1
    io_clock_set(clock(i))    
    io_clock_start(clock(i)) 'init game clock at 0  
  next  
  
  div1=" -----------------------------[ ||| eclipzer ||| ]-----------------------------"
  div2=" +-----------------------------------------------------------------+"
  div3=" |                                                                 |"
    
    
  clock(0).alarm=0*3600+0*60+3.25 'set time value
  clock(1).alarm=0*3600+0*60+10.07 'set time value
  clock(2).alarm=0*3600+0*60+25 'set time value
  
  
  io_clock_set clock(3),,,30
  io_clock_start clock(3),IO_BACKWARD
  clock(3).alarm=0 'set time value
  
  do
    
    for i=0 to CLOCKS-1
      io_clock_update(clock(i)) 'update game clock each cycle   
    next
    
    kb=inkey$
    
    select case kb
    case chr$(27):  done=1
    case "+":       io_clock_inc(clock(idx),,,15)
    case "-":       io_clock_inc(clock(idx),,,-15)    
    case " ":       io_clock_pause(clock(idx))
    case "x","X":   io_clock_stop(clock(idx))
    case chr$(13):  io_clock_set(clock(idx),,,clock(idx).start)
    case ASC_LEFT:  io_clock_pause(clock(idx)): io_clock_start(clock(idx),IO_BACKWARD)
    case ASC_RIGHT: io_clock_pause(clock(idx)): io_clock_start(clock(idx),IO_FORWARD)
    case ASC_UP:    idx=(idx-1) and (CLOCKS-1)
    case ASC_DOWN:  idx=(idx+1) and (CLOCKS-1)
    case else
    end select
    
    ScreenCopy FX_CLR_PAGE,FX_WRK_PAGE
    
    locate 1,1
    ?
    ? " Game Clock Routines"
    ? div1
    ?
    ? div2 ' +----+
    for i=0 to CLOCKS      
      ? div3 ' |    |
    next
    ? div2 ' +----+
    ?
    ? " Key Controls"
    ? "  UP ....... Cycle active clock (backward)"
    ? "  DOWN ..... Cycle active clock (forward)"
    ? "  LEFT ..... Set active clock to count backward"
    ? "  RIGHT .... Set active clock to count forward"    
    ? "  SPACE .... Toggle active clock on/off (pause)"
    ? "  ENTER .... Reset active clock"
    ? "  X ........ Stop active clock"
    ? "  + ........ Add 15 seconds to active clock"
    ? "  - ........ Subtract 15 seconds from active clock"
    ? "  ESC ...... Exit"
    ?
    
    xx=4
    yy=6
    locate yy,xx: ? "Clock      Time         Start         Alarm      State"
    
    for i=0 to CLOCKS-1    
      locate   ,xx: ? "  "& i &"    "+io_time_hms(clock(i).curr)+"  "+io_time_hms(clock(i).start)+"  "+io_time_hms(clock(i).alarm)+"  "+clock_state(clock(i))
      if io_bit(clock(i).flags,fALARM) then io_clock_stop(clock(i))
    next
    
    locate yy+idx+1,xx: ? ">" 'active clock cursor    
    
    locate 27,1
    ? div1
    ?
    ? " Copyright (c) Quinton Roberts 2006-2008."

    ScreenCopy
    
  loop until done
  
  function clock_state(t as clockdat) as string
    
    select case io_bit(t.flags,fUPDATE)'t.update
    case 0: return "Paused"
    case 1
      select case io_bit(t.flags,fCOUNT)
      case IO_FORWARD:  return "Count Forward"
      case IO_BACKWARD: return "Count Backward"
      case else:        return "Paused"
      end select
    end select
    
  end function  
  
' =============================================================================
'          Name: io_bit (02.21.08)
'       Returns: 
'    Parameters:
'          vdat: variable that contains bit data
'           idx: bit position
' -----------------------------------------------------------------------------
'   Description: return selected bit value (0=clear 1=set).
'      Comments: 
' =============================================================================  
  function io_bit (byref vdat as uinteger,idx as uinteger) as uinteger
    return (vdat shr idx) and 1
  end function
  
' =============================================================================
'          Name: io_bit_clr (02.21.08)
'       Returns: 
'    Parameters:
'          vdat: variable that contains bit data
'           idx: bit position
' -----------------------------------------------------------------------------
'   Description: Clear selected bit (value=0).
'      Comments: 
' =============================================================================  
  sub io_bit_clr(byref vdat as uinteger,idx as uinteger)
    vdat=(vdat and (not (1 SHL idx))) 'clear selected bit (set to 0)
  end sub
  
' =============================================================================
'          Name: io_bit_flp (02.21.08)
'       Returns: 
'    Parameters:
'          vdat: variable that contains bit data
'           idx: bit position
' -----------------------------------------------------------------------------
'   Description: Toggle selected bit.
'      Comments: Toggles a bit between clear and set values. A clear bit
'                becomes set (1), while a set bit becomes clear (0).
' =============================================================================  
  sub io_bit_flp(byref vdat as uinteger,idx as uinteger)
    vdat=(vdat xor (1 SHL idx)) 'toggle selected bit
  end sub
  
' =============================================================================
'          Name: io_bit_get (02.21.08)
'       Returns: 
'    Parameters:
'          vdat: variable that contains bit data
'           idx: bit position
'          bits: number of bits to return (sequence length)
' -----------------------------------------------------------------------------
'   Description: Return a sequence of bits from a variable.
'      Comments: Useful for extracting packed bit information.
' =============================================================================  
  function io_bit_get(byref vdat as uinteger,idx as uinteger,bits as uinteger) as uinteger
    return (vdat SHR idx) and ((2^bits)-1) 'retrieve bits at selected bit
  end function
  
' =============================================================================
'          Name: io_bit_put (02.21.08)
'       Returns: 
'    Parameters:
'          vdat: variable that contains bits data
'           idx: bit position
'          bits: number of bits to replace
'         value: replacement value
' -----------------------------------------------------------------------------
'   Description: Replace a sequence of bits in a variable.
'      Comments: Useful for inserting packed bit information.
' =============================================================================  
  sub io_bit_put(byref vdat as uinteger,idx as uinteger,bits as uinteger,value as uinteger)
    vdat=(vdat and (not((2^bits)-1) SHL idx)) 'clear selected bits
    vdat=(vdat or  (value SHL idx))           'store value at selected bit
  end sub
  
' =============================================================================
'          Name: io_bit_set (02.21.08)
'       Returns: 
'    Parameters:
'          vdat: variable that contains bit data
'           idx: bit position
' -----------------------------------------------------------------------------
'   Description: Set selected bit (value=1).
'      Comments: 
' =============================================================================  
  sub io_bit_set(byref vdat as uinteger,idx as uinteger)
    vdat=(vdat or (1 SHL idx)) 'set selected bit (set to 1)
  end sub

' =============================================================================
'          Name: io_clock_inc (03.25.08)
'       Returns: 
'    Parameters:
'             t: time structure
'            hh: init hours [0]
'            mm: init minutes [0]
'            ss: init seconds [0]
' -----------------------------------------------------------------------------
'   Description: Add set amount of time to play clock.
'      Comments: 
' =============================================================================
  sub io_clock_inc(t as clockdat,hh as double,mm as double,ss as double)
    t.value+=hh*3600+mm*60+ss    
  end sub
  
' =============================================================================
'          Name: io_clock_pause (03.25.08)
'       Returns: 
'    Parameters:
'             t: time structure
' -----------------------------------------------------------------------------
'   Description: Toggle time structure for updating.
'      Comments: 
' =============================================================================
  sub io_clock_pause(t as clockdat)
    io_bit_flp(t.flags,fUPDATE)    
    select case io_bit(t.flags,fUPDATE)
    case 0: t.value=t.curr: t.lapse=0
    case 1: io_clock_start(t,io_bit(t.flags,fCOUNT))
    end select
  end sub

' =============================================================================
'          Name: io_clock_set (03.25.08)
'       Returns: 
'    Parameters:
'             t: time structure
'            hh: init hours [0]
'            mm: init minutes [0]
'            ss: init seconds [0]
' -----------------------------------------------------------------------------
'   Description: Set a specific time for a time structure.
'      Comments: Only the time is set. To actually start the clock, use the
'                io_clock_start routine.
' =============================================================================
  sub io_clock_set(t as clockdat,hh as double,mm as double,ss as double)
    t.epoch=timer            'set new base time
    t.start=hh*3600+mm*60+ss 'set start time
    t.value=t.start          'set time value
  end sub

' =============================================================================
'          Name: io_clock_start (03.25.08)
'       Returns: 
'    Parameters:
'             t: time structure
'         count: counting direction -1=backward 1=forward [1]
' -----------------------------------------------------------------------------
'   Description: Init time structure for updating.
'      Comments: Call this routine to start a clock structure. 
' =============================================================================  
  sub io_clock_start(t as clockdat,count as integer=1)
    t.epoch=timer                      'set new base time
    io_bit_put(t.flags,fCOUNT,1,count) 'set clock direction    
    io_bit_set(t.flags,fUPDATE)        'set clock update flag   
  end sub

' =============================================================================
'          Name: io_clock_stop (03.25.08)
'       Returns: 
'    Parameters:
'             t: time structure
' -----------------------------------------------------------------------------
'   Description: Prevent time structure from updating.
'      Comments: 
' =============================================================================       
  sub io_clock_stop(t as clockdat)     
    t.value=t.curr              'save current time
    t.lapse=0                   'reset time lapse
    io_bit_clr(t.flags,fUPDATE) 'clear update flag   
  end sub  

' =============================================================================
'          Name: io_clock_update (03.25.08)
'       Returns: 
'    Parameters:
'             t: time structure
' -----------------------------------------------------------------------------
'   Description: Update current time structure.
'      Comments: This should be called once every program cycle. 
' =============================================================================
  sub io_clock_update(t as clockdat)
    
    dim as integer sign,alarm=0
    
    t.real=timer
    
    if io_bit(t.flags,fUPDATE) then 
      select case io_bit(t.flags,fCOUNT)
      case IO_FORWARD:  sign=1
      case IO_BACKWARD: sign=-1
      end select    
      t.lapse=sign*(t.real-t.epoch)
    end if
    
    t.curr=t.value+t.lapse     'update current time
    
    io_bit_clr(t.flags,fALARM) 'clear alarm flag
    
    select case t.start
    case is>t.alarm: if t.curr<=t.alarm then alarm=1
    case is<t.alarm: if t.curr>=t.alarm then alarm=1
    case else
    end select
    
    if alarm then io_bit_set(t.flags,fALARM): io_clock_set(t,,,t.alarm)
    
  end sub

' =============================================================================
'          Name: io_time_hms (03.23.08)
'       Returns: time string
'    Parameters:
'            tt: time, in seconds, to convert
' -----------------------------------------------------------------------------
'   Description: Converts seconds to a string in HH:MM:SS.SSS format.
'      Comments: 
' =============================================================================     
  function io_time_hms(tt as double) as string            
    
    dim as integer hh=fix(tt/3600): tt-=3600*hh 
    dim as integer mm=fix(tt/60):   tt-=60*mm
    dim as integer ss=fix(tt)
    dim as integer ms=int(abs(frac(tt)*1000))
      
    dim as string  hhn=str$(hh): if len(hhn)<2 then hhn="0"+hhn
    dim as string  mmn=str$(mm): if len(mmn)<2 then mmn="0"+mmn
    dim as string  ssn=str$(ss): if len(ssn)<2 then ssn="0"+ssn
    dim as string  msn=str$(ms): if len(msn)<2 then msn="0"+msn
                                if len(msn)<3 then msn="0"+msn                  
    
    return hhn+":"+mmn+":"+ssn+"."+msn
    
  end function 
-Eclipzer
Eclipzer
Posts: 432
Joined: Oct 01, 2005 10:50
Location: Maryland
Contact:

Post by Eclipzer »

I've fleshed out these routines a bit more. Now there's class support, which makes using clock objects much easier. Also, the clocks can now have a parent clock, so if the parent stops updating, so does the child. This is useful if you have various clocks that need to hooked into a master clock. For example, you pause your game, instead of manually stopping each clock, you just stop the master clock and all clocks hooked into it would also stop.

Finally, the syntax has been improved such that you no long have to manually update the clocks each cycle. Instead, you simply call a sync routine each cycle (io_sync). From here, a clock will only update if you request its time. If the clock has a parent clock, it will force the parent to update as well, unless the parent has already updated during the current cycle.

The following example is similar to the previous one, though it demonstrates the parent clock functionality. Each clock is linked to the clock before it. The first clock, therefore, has no parent and is naturally linked to the TIMER. Notice that the child clocks will stop when their parents do. The major differences, to me however, are in the code implementation. The class support simply makes reading, writing and using the code easier.

Code: Select all

' Program:  Game Clock Source Code
' Version:  0.08
' Language: FreeBASIC v0.18.3b
' Author:   Quinton Roberts (Eclipzer)
' Date:     11-13-06
' Update:   04-20-08
'
' Copyright (c) 2006-2008.

' define arrow keys
  #define ASC_UP     Chr$(255)+Chr$(72)
  #define ASC_DOWN   Chr$(255)+Chr$(80) 
  #define ASC_LEFT   Chr$(255)+Chr$(75) 
  #define ASC_RIGHT  Chr$(255)+Chr$(77)
  
  const VERSION="0.08"

' video page constants
  Const FX_VIS_PAGE=0
  Const FX_WRK_PAGE=1
  Const FX_CLR_PAGE=2 
 
' screen mode constants
  Const FX_320x240=14
  Const FX_640x480=18
  Const FX_800x600=19
  Const FX_FULLSCREEN=1
  
' general constants
  const IO_NULL=0
  
' clock constants
  const IO_TIMER=0    'define parent clock as FBs TIMER    
  const IO_TIME=1     'modify clock time
  const IO_RESET=2    'reset clock time
  const IO_START=3    'modify start time
  const IO_ALARM=4    'modify alarm time
  const IO_PARENT=5   'modify clock parent
  const IO_FORWARD=1  'set clock to count forward
  const IO_BACKWARD=0 'set clock to count backward
  
' clock flags  
  const fUPDATE=1  '0=off 1=update
  const fCOUNT=2   'clock count direction (0=backward 1=forward)
  const fPCOUNT=3  'parent clock count direction (0=backward 1=forward)
  const fALARM=4   '0=off 1=triggered  
  const fTIMEOUT=5 '0=off 1=on - set once alaram is triggered
  
  type clockClass
    as ulongint       sync   'sync value - keeps data samples uniform for each program cycle
    as clockClass ptr parent 'clock parent
    as integer        flags  'clock flags (update,count,alarm,timeout)
    as double         real   'parent clock sample
    as double         epoch  'initial time sample (base time)
    as double         lapse  'sampled difference between real and epoch values
    as double         start  'clock start time value (user defined)
    as double         alarm  'clock alarm time value (user defined)
    as double         value  'dynamic time value
    as double         curr   'current time
    
    declare sub      count(direction as integer=IO_FORWARD)
    declare sub      inc(hh as double=0,mm as double=0,ss as double=0,clockAttr as integer=IO_TIME)
    declare sub      pause()
    declare sub      set overload(hh as double=0,mm as double=0,ss as double=0,newParent as clockClass ptr=IO_TIMER,clockAttr as integer=IO_NULL)
    declare sub      set         (hh as double=0,mm as double=0,ss as double=0,clockAttr as integer)
    declare sub      set         (newParent as clockClass ptr)
    declare sub      stop()
    declare function time() as double
    declare sub      update()
  end type 

' Bit-wise function declarations
  declare function io_bit             (      myVar as ulongint,bitPosition as ubyte) as ubyte
    
  declare sub      io_bit_clr overload(byref myVar as ubyte,   bitPosition as ubyte)
  declare sub      io_bit_clr         (byref myVar as ushort,  bitPosition as ubyte)
  declare sub      io_bit_clr         (byref myVar as uinteger,bitPosition as ubyte)
  declare sub      io_bit_clr         (byref myVar as ulongint,bitPosition as ubyte)
  
  declare sub      io_bit_flp overload(byref myVar as ubyte,   bitPosition as ubyte)
  declare sub      io_bit_flp         (byref myVar as ushort,  bitPosition as ubyte)
  declare sub      io_bit_flp         (byref myVar as uinteger,bitPosition as ubyte)
  declare sub      io_bit_flp         (byref myVar as ulongint,bitPosition as ubyte)
  
  declare function io_bit_get overload(      myVar as ubyte,   bitPosition as ubyte,bits as ubyte) as ubyte
  declare function io_bit_get         (      myVar as ushort,  bitPosition as ubyte,bits as ubyte) as ushort
  declare function io_bit_get         (      myVar as uinteger,bitPosition as ubyte,bits as ubyte) as uinteger
  declare function io_bit_get         (      myVar as ulongint,bitPosition as ubyte,bits as ubyte) as ulongint
  
  declare sub      io_bit_put overload(byref myVar as ubyte,   bitPosition as ubyte,bits as ubyte,value as ubyte)
  declare sub      io_bit_put         (byref myVar as ushort,  bitPosition as ubyte,bits as ubyte,value as ushort)
  declare sub      io_bit_put         (byref myVar as uinteger,bitPosition as ubyte,bits as ubyte,value as uinteger)
  declare sub      io_bit_put         (byref myVar as ulongint,bitPosition as ubyte,bits as ubyte,value as ulongint)
    
  declare sub      io_bit_set overload(byref myVar as ubyte,   bitPosition as ubyte)
  declare sub      io_bit_set         (byref myVar as ushort,  bitPosition as ubyte)
  declare sub      io_bit_set         (byref myVar as uinteger,bitPosition as ubyte)
  declare sub      io_bit_set         (byref myVar as ulongint,bitPosition as ubyte)

' Clock routine declarations
  declare sub io_sync()
  declare function io_time_hms(tt as double) as string
  
  declare function clock_state(t as clockClass) as string
  
  dim shared as ulongint ioCycleCount
  
  const CLOCKS=4
   
  dim shared as clockClass clock(CLOCKS-1)
   
  dim as string  kb,state,div1,div2,div3
  dim as integer i,done,xx,yy,idx=0
  
  windowtitle "Game Clock Class v" & VERSION
  screen    FX_640x480,32,3,0
  screenset FX_WRK_PAGE,FX_VIS_PAGE
 
  for i=0 to CLOCKS-1
    if i then clock(i).set @clock(i-1) 'make parent clock the prev clock
    clock(i).count    
  next
  
  clock(0).set 0,0, 7.25,IO_ALARM 'set alarm value
  clock(1).set 0,0,10.07,IO_ALARM 'set alarm value
  clock(2).set 0,0, 3,   IO_ALARM 'set alarm value
  
  clock(3).set 0,0,30,IO_TIME
  clock(3).set 0,0, 0,IO_ALARM
  clock(3).count IO_BACKWARD
 
  div1=" -----------------------------[ ||| eclipzer ||| ]-----------------------------"
  div2=" +-----------------------------------------------------------------+"
  div3=" |                                                                 |"
    
  do
    
    io_sync
   
    kb=inkey$
   
    select case kb
    case chr$(27):  done=1
    case "+":       clock(idx).inc 0,0,15
    case "-":       clock(idx).inc 0,0,-15
    case " ":       clock(idx).pause
    case "x","X":   clock(idx).stop
    case chr$(13):  clock(idx).set 0,0,clock(idx).start    
    case ASC_LEFT:  clock(idx).count IO_BACKWARD
    case ASC_RIGHT: clock(idx).count IO_FORWARD
    case ASC_UP:    idx=(idx-1) And (CLOCKS-1)
    case ASC_DOWN:  idx=(idx+1) And (CLOCKS-1)
    case else
    end select
   
    ScreenCopy FX_CLR_PAGE,FX_WRK_PAGE
   
    Locate 1,1
    ?
    ? " Game Clock Routines"
    ? div1
    ?
    ? div2 ' +----+
    For i=0 To CLOCKS     
      ? div3 ' |    |
    Next
    ? div2 ' +----+
    ?
    ? " Key Controls"
    ? "  UP ....... Cycle active clock (backward)"
    ? "  DOWN ..... Cycle active clock (forward)"
    ? "  LEFT ..... Set active clock to count backward"
    ? "  RIGHT .... Set active clock to count forward"   
    ? "  SPACE .... Toggle active clock on/off (pause)"
    ? "  ENTER .... Reset active clock"
    ? "  X ........ Stop active clock"
    ? "  + ........ Add 15 seconds to active clock"
    ? "  - ........ Subtract 15 seconds from active clock"
    ? "  ESC ...... Exit"
    ?
   
    xx=4
    yy=6
    locate yy,xx: ? "Clock      Time         Start         Alarm      State"
   
    for i=0 To CLOCKS-1   
      locate   ,xx: ? "  "& i &"    "+io_time_hms(clock(i).time)+"  "+io_time_hms(clock(i).start)+"  "+io_time_hms(clock(i).alarm)+"  "+clock_state(clock(i))
      if io_bit(clock(i).flags,fALARM) Then clock(i).stop
    next
   
    locate yy+idx+1,xx: ? ">" 'active clock cursor   
   
    locate 27,1
    ? div1
    ?
    ? " Copyright (c) Quinton Roberts 2006-2008."

    screencopy
   
  loop until done
 
  function clock_state(t as clockClass) as string
   
    select case io_bit(t.flags,fUPDATE)'t.update
    case 0: return "Paused"
    case 1
      select case io_bit(t.flags,fCOUNT)
      case IO_FORWARD:  return "Count Forward"
      case IO_BACKWARD: return "Count Backward"
      case else:        return "Paused"
      end select
    end select
   
  end function
  
' =============================================================================
'          Name: .count (04.20.08)
'       Returns: 
'    Parameters:
'   [direction]: counting direction [IO_FORWARD] (IO_BACKWARD,IO_FORWARD)
' -----------------------------------------------------------------------------
'   Description: Init clock object for updating.
'      Comments: Call this routine to start a clock object. 
' =============================================================================  
  sub clockClass.count(direction as integer)
    if parent then epoch=parent->curr else epoch=timer 'set clock base time
    value=curr                           'save current time
    real=epoch                           'set clock real time
    lapse=0                              'reset time lapse
    io_bit_set(flags,fUPDATE)            'set clock update flag    
    io_bit_put(flags,fCOUNT,1,direction) 'set clock count direction
  end sub
  
' =============================================================================
'          Name: .inc (04.20.08)
'       Returns: 
'    Parameters:
'          [hh]: init hours [0]
'          [mm]: init minutes [0]
'          [ss]: init seconds [0]
'   [clockAttr]: clock attribute to increase [IO_TIME] (IO_TIME,IO_START,IO_ALARM)
' -----------------------------------------------------------------------------
'   Description: Add set amount of time to specified clock attribute.
'      Comments: 
' =============================================================================
  sub clockClass.inc(hh as double,mm as double,ss as double,clockAttr as integer)
    
    dim as double timeDelta
    
    timeDelta=hh*3600+mm*60+ss 'calculate time delta in seconds   
    
    select case clockAttr
    case IO_TIME:   value+=timeDelta
    case IO_START:  start+=timeDelta
    case IO_ALARM:  alarm+=timeDelta    
    case else
    end select
    
  end sub

' =============================================================================
'          Name: .pause (04.20.08)
'       Returns: 
'    Parameters:
' -----------------------------------------------------------------------------
'   Description: Toggle clock structure for updating.
'      Comments: 
' =============================================================================
  sub clockClass.pause()
    io_bit_flp(flags,fUPDATE)    
    select case io_bit(flags,fUPDATE)
    case 0: stop
    case 1: count(io_bit(flags,fCOUNT))
    end select
  end sub
  
' =============================================================================
'          Name: .set (04.20.08)
'       Returns: 
'    Parameters:
'          [hh]: init hours [0]
'          [mm]: init minutes [0]
'          [ss]: init seconds [0]
'   [newParent]: pointer to parent clock structure [0] (IO_TIMER=timer)
'   [clockAttr]: clock attribute to set [0] (IO_TIME,IO_START,IO_ALARM,IO_RESET,IO_PARENT)
' -----------------------------------------------------------------------------
'   Description: Set the attributes of a clock structure.
'      Comments: This routines allows you to define the time, start time and
'                parent clock of a time structure. Use the clockAttr parameter
'                to ONLY modify a specific clock attribute.
'
'                clockAttr can take on the following values:
'                  IO_TIME .... only modify clock time
'                  IO_START ... only modify clock start time
'                  IO_ALARM ... only modify clock alarm time
'                  IO_RESET ... only modify clock time - reset it to clock start time
'                  IO_PARENT .. only modify clock parent
' =============================================================================
  sub clockClass.set(hh as double,mm as double,ss as double,newParent as clockClass ptr,clockAttr as integer)
          
    dim as double newTime,newEpoch
    
    if newParent then parent=newParent 'assign parent clock if specified    
    if parent then                     'if parent clock present... 
      newEpoch=parent->curr            'get new base time from parent clock
    else                               'else...
      newEpoch=timer                   'get new base time from timer      
    end if
    
    newTime=hh*3600+mm*60+ss 'calculate time in seconds
    
    select case clockAttr
    case IO_START:  start=newTime
    case IO_ALARM:  alarm=newTime:  io_bit_clr(flags,fTIMEOUT)
    case IO_TIME:   value=newTime:  curr=value: epoch=newEpoch: real=newEpoch
    case IO_RESET:  value=start:    curr=value: epoch=newEpoch: real=newEpoch: io_bit_clr(flags,fTIMEOUT)
    case IO_PARENT: epoch=newEpoch: real=newEpoch
    case else:      value=newTime:  curr=value: epoch=newEpoch: real=newEpoch: start=newTime
    end select
    
  end sub
  
  sub clockClass.set(hh as double,mm as double,ss as double,clockAttr as integer)
    set hh,mm,ss,,clockAttr
  end sub  
  
  sub clockClass.set(newParent as clockClass ptr)
    set ,,,newParent,IO_PARENT
  end sub  

' =============================================================================
'          Name: .stop (04.20.08)
'       Returns: 
'    Parameters:
' -----------------------------------------------------------------------------
'   Description: Prevent clock structure from updating.
'      Comments: 
' =============================================================================       
  sub clockClass.stop()
    value=curr                'save current time
    lapse=0                   'reset time lapse
    io_bit_clr(flags,fUPDATE) 'clear update flag   
  end sub
  
' =============================================================================
'          Name: .time (04.20.08)
'       Returns: 
'    Parameters:
' -----------------------------------------------------------------------------
'   Description: Returns time in seconds of specified clock structure.
'      Comments: This routine forces the clock structure to update before
'                returning its time value. This removes the need to manually
'                update a clock every program cycle.
' =============================================================================  
  function clockClass.time() as double
    update 
    return curr
  end function
  
' =============================================================================
'          Name: .update (04.20.08)
'       Returns: 
'    Parameters:
' -----------------------------------------------------------------------------
'   Description: Update specified clock structure.
'      Comments: Use the io_sync routine to allow dynamic clock updates.
' =============================================================================
  sub clockClass.update()
    
    dim as integer sign,pcount,timeout=0
    
    if sync<>ioCycleCount then    
      sync=ioCycleCount    
      if parent then
        *parent.update        
        real=parent->curr 
      else 
        real=timer
      end if       
    end if
  
    if io_bit(flags,fUPDATE) then
    ' account for clock count direction  
      select case io_bit(flags,fCOUNT)
      case IO_FORWARD:  sign=1
      case IO_BACKWARD: sign=-1
      end select      
    ' account for parent clock count direction
      if parent then
        pcount=io_bit(parent->flags,fCOUNT)
        if pcount=IO_BACKWARD then sign=-sign
        if io_bit(flags,fPCOUNT)<>pcount then 'if parent clock count direction has changed...   
          io_bit_put(flags,fPCOUNT,1,pcount)  'set parent clock count direction in child flags
          value=curr                          'save current time
          lapse=0                             'reset time lapse
          epoch=parent->curr                  'reset clock base time
          real=epoch                          'reset "real" time
        end if        
      end if      
      lapse=sign*(real-epoch) 'update time lapse
    end if
    
    curr=value+lapse     'update current time
    
    io_bit_clr(flags,fALARM) 'clear alarm flag
    
    if io_bit(flags,fTIMEOUT)=0 then    
      select case start
      case is>alarm: if curr<=alarm then timeout=1
      case is<alarm: if curr>=alarm then timeout=1
      case else
      end select    
    end if
   
    if timeout then
      io_bit_set(flags,fALARM)   'set alarm flag
      io_bit_set(flags,fTIMEOUT) 'set timeout flag      
    end if    
    
  end sub
  
' =============================================================================
'          Name: io_bit (04.09.08)
'       Returns: 
'    Parameters:
'         myVar: variable containing bit data
'   bitPosition: bit position
' -----------------------------------------------------------------------------
'   Description: Returns the bit value at bit position (0=clear 1=set).
'      Comments: The 1st bit position is 0.
' =============================================================================
  function io_bit (myVar as ulongint,bitPosition as ubyte) as ubyte
    return (myVar shr bitPosition) and 1
  end function
  
' =============================================================================
'          Name: io_bit_clr (04.09.08)
'       Returns: 
'    Parameters:
'         myVar: variable containing bit data
'   bitPosition: bit position
' -----------------------------------------------------------------------------
'   Description: Clear selected bit (value=0).
'      Comments: 
' =============================================================================  
  sub io_bit_clr(byref myVar as ubyte,bitPosition as ubyte)
    myVar=(myVar and (not (1 shl bitPosition))) 'clear selected bit (set to 0)
  end sub
  
  sub io_bit_clr(byref myVar as ushort,bitPosition as ubyte)
    myVar=(myVar and (not (1 shl bitPosition))) 'clear selected bit (set to 0)
  end sub
  
  sub io_bit_clr(byref myVar as uinteger,bitPosition as ubyte)
    myVar=(myVar and (not (1 shl bitPosition))) 'clear selected bit (set to 0)
  end sub
  
  sub io_bit_clr(byref myVar as ulongint,bitPosition as ubyte)
    myVar=(myVar and (not (1 shl bitPosition))) 'clear selected bit (set to 0)
  end sub
  
' =============================================================================
'          Name: io_bit_flp (04.09.08)
'       Returns: 
'    Parameters:
'         myVar: variable containing bit data
'   bitPosition: bit position
' -----------------------------------------------------------------------------
'   Description: Toggle selected bit.
'      Comments: Toggles a bit between clear and set values. A clear bit
'                becomes set (1), while a set bit becomes clear (0).
' =============================================================================  
  sub io_bit_flp(byref myVar as ubyte,bitPosition as ubyte)
    myVar=(myVar xor (1 shl bitPosition)) 'toggle selected bit
  end sub
  
  sub io_bit_flp(byref myVar as ushort,bitPosition as ubyte)
    myVar=(myVar xor (1 shl bitPosition)) 'toggle selected bit
  end sub
  
  sub io_bit_flp(byref myVar as uinteger,bitPosition as ubyte)
    myVar=(myVar xor (1 shl bitPosition)) 'toggle selected bit
  end sub
  
  sub io_bit_flp(byref myVar as ulongint,bitPosition as ubyte)
    myVar=(myVar xor (1 shl bitPosition)) 'toggle selected bit
  end sub
  
' =============================================================================
'          Name: io_bit_get (04.09.08)
'       Returns: 
'    Parameters:
'         myVar: variable containing bit data
'   bitPosition: bit position
'          bits: number of bits to return (sequence length)
' -----------------------------------------------------------------------------
'   Description: Return a sequence of bits from a variable.
'      Comments: Useful for extracting packed bit information.
' =============================================================================  
  function io_bit_get(myVar as ubyte,bitPosition as ubyte,bits as ubyte) as ubyte
    return (myVar shr bitPosition) and ((2^bits)-1) 'retrieve bits at selected bit
  end function
  
  function io_bit_get(myVar as ushort,bitPosition as ubyte,bits as ubyte) as ushort
    return (myVar shr bitPosition) and ((2^bits)-1) 'retrieve bits at selected bit
  end function
  
  function io_bit_get(myVar as uinteger,bitPosition as ubyte,bits as ubyte) as uinteger
    return (myVar shr bitPosition) and ((2^bits)-1) 'retrieve bits at selected bit
  end function
  
  function io_bit_get(myVar as ulongint,bitPosition as ubyte,bits as ubyte) as ulongint
    return (myVar shr bitPosition) and ((2^bits)-1) 'retrieve bits at selected bit
  end function
  
' =============================================================================
'          Name: io_bit_put (04.09.08)
'       Returns: 
'    Parameters:
'         myVar: variable containing bit data
'   bitPosition: bit position
'          bits: number of bits to replace
'         value: replacement value
' -----------------------------------------------------------------------------
'   Description: Replace a sequence of bits in a variable.
'      Comments: Useful for inserting packed bit information.
' =============================================================================  
  sub io_bit_put(byref myVar as ubyte,bitPosition as ubyte,bits as ubyte,value as ubyte)
    myVar=(myVar and (not((2^bits)-1) shl bitPosition)) 'clear selected bits
    myVar=(myVar or  (value shl bitPosition))           'store value at selected bit
  end sub
  
  sub io_bit_put(byref myVar as ushort,bitPosition as ubyte,bits as ubyte,value as ushort)
    myVar=(myVar and (not((2^bits)-1) shl bitPosition)) 'clear selected bits
    myVar=(myVar or  (value shl bitPosition))           'store value at selected bit
  end sub
  
  sub io_bit_put(byref myVar as uinteger,bitPosition as ubyte,bits as ubyte,value as uinteger)
    myVar=(myVar and (not((2^bits)-1) shl bitPosition)) 'clear selected bits
    myVar=(myVar or  (value shl bitPosition))           'store value at selected bit
  end sub
  
  sub io_bit_put(byref myVar as ulongint,bitPosition as ubyte,bits as ubyte,value as ulongint)
    myVar=(myVar and (not((2^bits)-1) shl bitPosition)) 'clear selected bits
    myVar=(myVar or  (value shl bitPosition))           'store value at selected bit
  end sub
  
' =============================================================================
'          Name: io_bit_set (04.09.08)
'       Returns: 
'    Parameters:
'         myVar: variable containing bit data
'   bitPosition: bit position
' -----------------------------------------------------------------------------
'   Description: Set selected bit (value=1).
'      Comments: 
' =============================================================================  
  sub io_bit_set(byref myVar as ubyte,bitPosition as ubyte)
    myVar=(myVar or (1 SHL bitPosition)) 'set selected bit (set to 1)
  end sub
  
  sub io_bit_set(byref myVar as ushort,bitPosition as ubyte)
    myVar=(myVar or (1 SHL bitPosition)) 'set selected bit (set to 1)
  end sub
  
  sub io_bit_set(byref myVar as uinteger,bitPosition as ubyte)
    myVar=(myVar or (1 SHL bitPosition)) 'set selected bit (set to 1)
  end sub
  
  sub io_bit_set(byref myVar as ulongint,bitPosition as ubyte)
    myVar=(myVar or (1 SHL bitPosition)) 'set selected bit (set to 1)
  end sub
  
' =============================================================================
'          Name: io_sync (04.20.08)
'       Returns: 
'    Parameters:
' -----------------------------------------------------------------------------
'   Description: Update global cycle count.
'      Comments: This routine must be called once every program cycle. A
'                constant cycle count prevents I/O objects from repeatedly
'                updating during the same program cycle, thus keeping their data
'                values uniform throughout the cycle.
' =============================================================================
  sub io_sync()
    ioCycleCount=(ioCycleCount+1) and &HFFFFFFFF 'increment cycle count - loop after 2^32 cycles   
  end sub
  
' =============================================================================
'          Name: io_time_hms (03.23.08)
'       Returns: time string
'    Parameters:
'            tt: time, in seconds, to convert
' -----------------------------------------------------------------------------
'   Description: Converts seconds to a string in HH:MM:SS.SSS format.
'      Comments: 
' =============================================================================     
  function io_time_hms(tt as double) as string            
    
    dim as integer hh=fix(tt/3600): tt-=3600*hh 
    dim as integer mm=fix(tt/60):   tt-=60*mm
    dim as integer ss=fix(tt)
    dim as integer ms=int(abs(frac(tt)*1000))
      
    dim as string  hhn=str$(hh): if len(hhn)<2 then hhn="0"+hhn
    dim as string  mmn=str$(mm): if len(mmn)<2 then mmn="0"+mmn
    dim as string  ssn=str$(ss): if len(ssn)<2 then ssn="0"+ssn
    dim as string  msn=str$(ms): if len(msn)<2 then msn="0"+msn
                                 if len(msn)<3 then msn="0"+msn
    
    return hhn+":"+mmn+":"+ssn+"."+msn
    
  end function 
badmrbox
Posts: 664
Joined: Oct 27, 2005 14:40
Location: Sweden
Contact:

Post by badmrbox »

Yay, awesome Eclipzer.
Eclipzer
Posts: 432
Joined: Oct 01, 2005 10:50
Location: Maryland
Contact:

Post by Eclipzer »

Thanks for the love. =)
anonymous1337
Posts: 5494
Joined: Sep 12, 2005 20:06
Location: California

Post by anonymous1337 »

Game clocks are neat. My latest game clock allows you to get the average of frames so far, as time-based movement can lead to skippiness sometimes.

Right now it's a simple: if( frameCount > 60 ) then resetFrameCount()

EDIT: Just realized that I my timer's latest version is in Java, not FreeBASIC :(


There's a lot of talk in game forums, I've seen, about getting accurate timing. Now I don't quite understand all the hubbub. Sure, timer() has limited accuracy, and it might be best to get the processor ticks instead.

However, with frequent checks on the timer and many frames within a short period, along with using proper timing methods (you should never rely on frame consistency/time accuracy unless you're a console developer), I don't see the need.
Post Reply