Here's my take on a non percentage based solution. Attached a basic project to illustrate it.
Edit: Found a bug. Seems like on android it takes an extra frame to reset the pointer button commands.
Edit: Fixed.
#constant IS_TRUE 1
#constant IS_FALSE 0
#constant IS_LEFT 1
#constant IS_RIGHT 0
#constant IS_UP 1
#constant IS_DOWN 0
type t_Inertial_Scrolling
vel_x as float
vel_y as float
pos_x as float
pos_y as float
initClick_pos_x as float
initClick_pos_y as float
pos_prev_x as float
pos_prev_y as float
pos_delta_x as float
pos_delta_y as float
pos_deltaStore_x as float
pos_deltaStore_y as float
speed_x as float
speed_y as float
dampening_x as float
dampening_y as float
stopRange_min_x as float
stopRange_Max_x as float
stopRange_min_y as float
stopRange_Max_y as float
move_scalar as float
distanceTillDrag as float
active as integer
dragged as integer
horizontal as integer
vertical as integer
moveDirection_x as integer
moveDirection_y as integer
frameSkip as integer
endtype
remstart
HOW-TO-USE EXAMPLE:
global scroller as t_Inertial_Scrolling // Make a type variable
Initialize_Default_InertialScroller( scroller ) // Supply the functions with the new type varible you just created.
do
handle_Inertial_Scrolling( scroller, GetPointerState(), GetPointerPressed(), GetPointerReleased(), GetPointerX(), GetPointerY() )
set_InertialScroller_ViewOffset( scroller )
Sync()
loop
remend
// This is just a convenience. Change the parameters of the function calls to fit your needs.
function Initialize_Default_InertialScroller( _insc ref as t_Inertial_Scrolling )
// This controls all updating.
set_InertialScroller_Active( _insc, IS_TRUE )
// Controls whether the x or y axis update.
set_InertialScroller_Orientation( _insc, IS_TRUE, IS_TRUE )
// Controls which direction the scroller is moved.
set_InertialScroller_MoveDirection( _insc, IS_RIGHT, IS_DOWN )
// Sets the position of the scroller. It's best to only call this once during initialization.
set_InertialScroller_Position( _insc, GetVirtualWidth() * 0.5, GetVirtualHeight() * 0.5 )
// Sets the x velocity to zero when it is in this range.
set_InertialScroller_StopRange_X( _insc, -1.0, 1.0 )
// Sets the y velocity to zero when it is in this range.
set_InertialScroller_StopRange_Y( _insc, -1.0, 1.0 )
// Sets how far the poiter will need to move away from the initial click position for it to be considered a drag.
set_InertialScroller_DistanceTillDrag( _insc, 25.0 )
select GetDeviceBaseName()
case "android"
// This controls how fast the scroller scrolls.
set_InertialScroller_Speed( ViewOffsetScroller, 1.0, 1.0 )
// Controls how far the scroller moves when pressing the device.
set_InertialScroller_MoveScalar( ViewOffsetScroller, 10.0 )
// This controls how fast the scroller slows down.
set_InertialScroller_Dampening( ViewOffsetScroller, 1.0, 1.0 )
endcase
case default
set_InertialScroller_Speed( ViewOffsetScroller, 2.5, 2.5 )
set_InertialScroller_MoveScalar( ViewOffsetScroller, 1.0 )
set_InertialScroller_Dampening( ViewOffsetScroller, 0.5, 0.5 )
endcase
endselect
endfunction
function handle_Inertial_Scrolling( _insc ref as t_Inertial_Scrolling, _keyState, _keyPressed, _keyReleased, _posx#, _posy# )
if _insc.active = IS_TRUE
if _keyState
// Key is first pressed.
if _keyPressed
_insc.initClick_pos_x = _posx#
_insc.initClick_pos_y = _posy#
_insc.pos_prev_x = _posx#
_insc.pos_prev_y = _posy#
set_InertialScroller_Velocity( _insc, 0.0, 0.0 )
_insc.pos_deltaStore_x = 0.0
_insc.pos_deltaStore_y = 0.0
endif
// Get the pointer pos delta to track movement.
_insc.pos_delta_x = _insc.pos_prev_x - _posx#
_insc.pos_delta_y = _insc.pos_prev_y - _posy#
if not _insc.pos_prev_x = _posx# or not _insc.pos_prev_y = _posy#
_insc.pos_deltaStore_x = _insc.pos_delta_x
_insc.pos_deltaStore_y = _insc.pos_delta_y
endif
_insc.pos_prev_x = _posx#
_insc.pos_prev_y = _posy#
// Update x position.
if _insc.horizontal = IS_TRUE
if _insc.moveDirection_x
dec _insc.pos_x, _insc.pos_delta_x * _insc.move_scalar * ( _insc.speed_x * 0.1 )
else
inc _insc.pos_x, _insc.pos_delta_x * _insc.move_scalar * ( _insc.speed_x * 0.1 )
endif
endif
// Update y position.
if _insc.vertical = IS_TRUE
if _insc.moveDirection_y
dec _insc.pos_y, _insc.pos_delta_y * _insc.move_scalar * ( _insc.speed_x * 0.1 )
else
inc _insc.pos_y, _insc.pos_delta_y * _insc.move_scalar * ( _insc.speed_x * 0.1 )
endif
endif
select GetDeviceBaseName()
case "android"
// If delta has changed then the event is considered a drag.
if _insc.pos_delta_x <> 0.0 or _insc.pos_delta_y <> 0.0
if GetDistance( _posx#, _posy#, _insc.initClick_pos_x, _insc.initClick_pos_y ) > _insc.distanceTillDrag
_insc.dragged = IS_TRUE
endif
endif
endcase
case default
// If delta has changed then the event is considered a drag.
if _insc.pos_delta_x <> 0.0 or _insc.pos_delta_y <> 0.0
if GetDistance( _posx#, _posy#, _insc.initClick_pos_x, _insc.initClick_pos_y ) > _insc.distanceTillDrag
_insc.dragged = IS_TRUE
endif
elseif _insc.pos_delta_x = 0.0 and _insc.pos_delta_y = 0.0
_insc.dragged = IS_FALSE
_insc.pos_deltaStore_x = 0.0
_insc.pos_deltaStore_y = 0.0
endif
endcase
endselect
elseif _keyReleased
// If dragged then set the velocity.
if _insc.dragged = IS_TRUE
_xv# = 0.0 : _yv# = 0.0
// Calculate x velocity from delta.
if _insc.horizontal
_xv# = _insc.pos_deltaStore_x * _insc.speed_x * _insc.move_scalar * 10.0
if _insc.moveDirection_x = IS_RIGHT then _xv# = -( _xv# )
endif
// Calculate y velocity from delta.
if _insc.vertical
_yv# = _insc.pos_deltaStore_y * _insc.speed_y * _insc.move_scalar * 10.0
if _insc.moveDirection_y = IS_DOWN then _yv# = -( _yv# )
endif
if _xv# <> 0.0 and _yv# <> 0.0 then set_InertialScroller_Velocity( _insc, _xv#, _yv# )
else
set_InertialScroller_Velocity( _insc, 0.0, 0.0 )
endif
_insc.dragged = IS_FALSE
endif
// Update x velocity and x position for smooth scrolling.
if _insc.horizontal
if _insc.vel_x <> 0.0
dec _insc.vel_x, _insc.vel_x * _insc.dampening_x * GetFrameTime()
if _insc.vel_x > _insc.stopRange_min_x and _insc.vel_x < _insc.stopRange_max_x then _insc.vel_x = 0.0
dec _insc.pos_x, _insc.vel_x * GetFrameTime()
endif
endif
// Update y velocity and y position for smooth scrolling.
if _insc.vertical
if _insc.vel_y <> 0.0
dec _insc.vel_y, _insc.vel_y * _insc.dampening_y * GetFrameTime()
if _insc.vel_y > _insc.stopRange_min_y and _insc.vel_y < _insc.stopRange_max_y then _insc.vel_y = 0.0
dec _insc.pos_y, _insc.vel_y * GetFrameTime()
endif
endif
endif
endfunction
// Controls whether the x or y axis update.
function set_InertialScroller_Orientation( _insc ref as t_Inertial_Scrolling, _hori, _vert )
if _hori <= IS_FALSE
_insc.horizontal = IS_FALSE
elseif _hori > IS_FALSE
_insc.horizontal = IS_TRUE
endif
if _vert <= IS_FALSE
_insc.vertical = IS_FALSE
elseif _vert > IS_FALSE
_insc.vertical = IS_TRUE
endif
endfunction
// Controls which direction the scroller is moved.
function set_InertialScroller_MoveDirection( _insc ref as t_Inertial_Scrolling, _dirx, _diry )
if _dirx <= IS_FALSE
_insc.moveDirection_x = IS_FALSE
elseif _dirx > IS_FALSE
_insc.moveDirection_x = IS_TRUE
endif
if _diry <= IS_FALSE
_insc.moveDirection_y = IS_FALSE
elseif _diry > IS_FALSE
_insc.moveDirection_y = IS_TRUE
endif
endfunction
// Sets the x velocity to zero when it is in this range.
function set_InertialScroller_StopRange_X( _insc ref as t_Inertial_Scrolling, _min#, _max# )
_insc.stopRange_min_x = _min#
_insc.stopRange_Max_x = _max#
endfunction
// Sets the y velocity to zero when it is in this range.
function set_InertialScroller_StopRange_Y( _insc ref as t_Inertial_Scrolling, _min#, _max# )
_insc.stopRange_min_y = _min#
_insc.stopRange_Max_y = _max#
endfunction
// Sets the position of the scroller. It's best to only call this once during initialization.
function set_InertialScroller_Position( _insc ref as t_Inertial_Scrolling, _posx#, _posy# )
_insc.pos_x = _posx#
_insc.pos_y = _posy#
endfunction
// This controls how fast the scroller scrolls.
function set_InertialScroller_Speed( _insc ref as t_Inertial_Scrolling, _speedx#, _speedy# )
_insc.speed_x = abs( _speedx# )
_insc.speed_y = abs( _speedy# )
endfunction
// This controls how fast the scroller slows down.
function set_InertialScroller_Dampening( _insc ref as t_Inertial_Scrolling, _dampx#, _dampy# )
if _dampx# < 0.0
_insc.dampening_x = 0.0
else
_insc.dampening_x = _dampx#
endif
if _dampy# < 0.0
_insc.dampening_y = 0.0
else
_insc.dampening_y = _dampy#
endif
endfunction
// This controls all updating.
function set_InertialScroller_Active( _insc ref as t_Inertial_Scrolling, _state )
if _state <= IS_FALSE
_insc.active = IS_FALSE
elseif _state > IS_FALSE
_insc.active = IS_TRUE
endif
endfunction
// This is used to set the velocity. It's best to not call this at all.
function set_InertialScroller_Velocity( _insc ref as t_Inertial_Scrolling, _velx#, _vely# )
_insc.vel_x = _velx#
_insc.vel_y = _vely#
endfunction
// Controls how far the scroller moves when pressing the device.
function set_InertialScroller_MoveScalar( _insc ref as t_Inertial_Scrolling, _scalar# )
if _scalar# < 0.0
_insc.move_scalar = 0.0
else
_insc.move_scalar = _scalar#
endif
endfunction
// Sets how far the poiter will need to move away from the initial click position for it to be considered a drag.
function set_InertialScroller_DistanceTillDrag( _insc ref as t_Inertial_Scrolling, _dist# )
if _dist# < 0.0
_insc.distanceTillDrag = 5.0
else
_insc.distanceTillDrag = _dist#
endif
endfunction
// Sets the view offset to the scroller position.
function set_InertialScroller_ViewOffset( _insc as t_Inertial_Scrolling )
SetViewOffset( _insc.pos_x, _insc.pos_y )
endfunction
// Returns the dragged variable.
function Get_InertialScroller_Dragged( _insc as t_Inertial_Scrolling )
endfunction _insc.dragged
// Prints various scroller variables.
function Print_InertialScroller( _insc as t_Inertial_Scrolling, _state )
if _state = IS_TRUE
DrawEllipse( _insc.pos_x, _insc.pos_y, 5.0, 5.0, mclr_red, mclr_red, 0 )
_newDiffx# = _insc.vel_x * 0.1
if _insc.moveDirection_x = IS_RIGHT then _newPosx# = -( _newPosx# )
_newDiffy# = _insc.vel_y * 0.1
if _insc.moveDirection_y = IS_DOWN then _newPosy# = -( _newPosy# )
DrawLine( _insc.pos_x, _insc.pos_y, _insc.pos_x - _newDiffx#, _insc.pos_y - _newDiffy#, 255, 255, 255 )
PrintC( "Active: " ) : Print( str_boolean( _insc.active ) )
PrintC( "Dragged: " ) : Print( str_boolean( _insc.dragged ) )
PrintC( "Delta: " ) : Print( str_vec2( _insc.pos_delta_x, _insc.pos_delta_y, 3 ) )
PrintC( "DeltaStore: " ) : Print( str_vec2( _insc.pos_deltaStore_x, _insc.pos_deltaStore_y, 3 ) )
PrintC( "Pos: " ) : Print( str_vec2( _insc.pos_x, _insc.pos_y, 3 ) )
PrintC( "Vel: " ) : Print( str_vec2( _insc.vel_x, _insc.vel_y, 3 ) )
PrintC( "Dampening: " ) : Print( str_vec2( _insc.dampening_x, _insc.dampening_y, 3 ) )
PrintC( "Speed: " ) : Print( str_vec2( _insc.speed_x, _insc.speed_y, 3 ) )
endif
endfunction
function str_boolean( _int )
if _int > IS_FALSE then exitfunction "true"
endfunction "false"
function str_vec2( _x#, _y#, _decimals )
_r$ = "{" + str( _x#, _decimals ) + ", " + str( _y#, _decimals ) + "}"
endfunction _r$
function GetDistance( _x1#, _y1#, _x2#, _y2# )
_r# = sqrt( pow( _x2# - _x1#, 2 ) + pow( _y2# - _y1#, 2 ) )
endfunction _r#