Strict
Import mojo
Class VirtualStickTestApp Extends App
Const PLAYFIELD_WIDTH:Float = 200
Const PLAYFIELD_HEIGHT:Float = 200
Const PLAYER_SPEED:Float = 5
' our virtual stick
Field mystick:MyStick
' the "player"'s location
Field playerX:Float = PLAYFIELD_WIDTH/2
Field playerY:Float = PLAYFIELD_HEIGHT/2
Field playfieldX:Float
Field playfieldY:Float = 10
Method OnCreate:Int()
mystick = New MyStick
mystick.SetRing(100, DeviceHeight()-100, 40)
mystick.SetStick(0, 0, 15)
mystick.SetDeadZone(0.2)
mystick.SetTriggerDistance(5)
playfieldX = DeviceWidth()-PLAYFIELD_WIDTH-10
SetUpdateRate 30
Return 0
End
Method OnUpdate:Int()
' update the stick usage
UpdateStick()
' update the player position
If mystick.GetVelocity() <> 0 Then
playerX += mystick.GetDX() * PLAYER_SPEED
playerY -= mystick.GetDY() * PLAYER_SPEED
If playerX < 0 Then
playerX = 0
Elseif playerX > PLAYFIELD_WIDTH Then
playerX = PLAYFIELD_WIDTH
End
If playerY < 0 Then
playerY = 0
Elseif playerY > PLAYFIELD_HEIGHT Then
playerY = PLAYFIELD_HEIGHT
End
End
Return 0
End
Method OnRender:Int()
Cls(0,0,0)
mystick.DoRenderRing()
mystick.DoRenderStick()
DrawOutlineRect(playfieldX, playfieldY, PLAYFIELD_WIDTH, PLAYFIELD_HEIGHT)
DrawCircle(playfieldX + playerX, playfieldY + playerY, 5)
' some test info
DrawText("angle="+mystick.GetAngle(), 10, 10)
DrawText("vel="+mystick.GetVelocity(), 10, 30)
DrawText("dx="+mystick.GetDX(), 10, 50)
DrawText("dy="+mystick.GetDY(), 10, 70)
Return 0
End
Method UpdateStick:Void()
If mystick.GetTouchNumber() < 0 Then
#if TARGET="android" Then
For Local i:Int = 0 To 31
If TouchHit(i) And mystick.GetTouchNumber() < 0 Then
mystick.StartTouch(TouchX(i), TouchY(i), i)
End
End
#else
If MouseHit(0) Then
mystick.StartTouch(MouseX(), MouseY(), 0)
End
#endif
End
If mystick.GetTouchNumber() >= 0 Then
#if TARGET="android" Then
If TouchDown(mystick.GetTouchNumber()) Then
mystick.UpdateTouch(TouchX(mystick.GetTouchNumber()), TouchY(mystick.GetTouchNumber()))
Else
mystick.StopTouch()
End
#else
If MouseDown(0) Then
mystick.UpdateTouch(MouseX(), MouseY())
Else
mystick.StopTouch()
End
#endif
End
End
End
Class MyStick Extends VirtualStick
Method RenderRing:Void(x:Float, y:Float)
SetColor 0, 0, 255
Super.RenderRing(x, y)
SetColor 255, 255, 255
End
Method RenderStick:Void(x:Float, y:Float)
SetColor 0, 255, 0
Super.RenderStick(x, y)
SetColor 255, 255, 255
End
End
Class VirtualStick
Private
' the coordinates and dimensions for the virtual stick's ring (where the user will first touch)
Field ringX:Float
Field ringY:Float
Field ringRadius:Float
' the coordinates and dimensions for the stick (what the user is pushing around)
' X/Y is relative to the centre of the ring, and positive Y points up
Field stickX:Float = 0
Field stickY:Float = 0
Field stickRadius:Float
Field stickAngle:Float
Field stickPower:Float
' where the user first touched
Field firstTouchX:Float
Field firstTouchY:Float
' power must always be >= this, or we return 0
Field deadZone:Float
' we need to move the stick this much before it triggers
Field triggerDistance:Float = -1
Field triggered:Bool = False
' the index of the touch event that initiated the stick movement
Field touchNumber:Int = -1
' clips the stick to be within the ring, and updates angles, etc.
Method UpdateStick:Void()
If touchNumber>=0 Then
Local length:Float = Sqrt(stickX*stickX+stickY*stickY)
stickPower = length/ringRadius
If stickPower > 1 Then stickPower = 1
If stickPower < deadZone Then
stickPower = 0
stickAngle = 0
stickX = 0
stickY = 0
Else
If stickX = 0 And stickY = 0 Then
stickAngle = 0
stickPower = 0
Elseif stickX = 0 And stickY > 0 Then
stickAngle = 90
Elseif stickX = 0 And stickY < 0 Then
stickAngle = 270
Elseif stickY = 0 And stickX > 0 Then
stickAngle = 0
Elseif stickY = 0 And stickX < 0 Then
stickAngle = 180
Elseif stickX > 0 And stickY > 0 Then
stickAngle = ATan(stickY/stickX)
Elseif stickX < 0 Then
stickAngle = 180+ATan(stickY/stickX)
Else
stickAngle = 360+ATan(stickY/stickX)
End
If length > ringRadius Then
stickPower = 1
stickX = Cos(stickAngle) * ringRadius
stickY = Sin(stickAngle) * ringRadius
End
End
End
End
Public
Method GetTouchNumber:Int()
Return touchNumber
End
' the angle in degrees that the user is pushing, going counter-clockwise from right
Method GetAngle:Float()
Return stickAngle
End
' the strength of the movement (0 means dead centre, 1 means at the edge of the ring (or past it)
Method GetVelocity:Float()
Return stickPower
End
' based on the angle and velocity, get the DX
Method GetDX:Float()
Return Cos(stickAngle) * stickPower
End
' based on the angle and velocity, get the DY
Method GetDY:Float()
Return Sin(stickAngle) * stickPower
End
' we just touched the screen at point (x,y), so start "controlling" if we touched inside the ring
Method StartTouch:Void(x:Float, y:Float, touchnum:Int)
If touchNumber < 0 Then
If (x-ringX)*(x-ringX) + (y-ringY)*(y-ringY) <= ringRadius*ringRadius Then
touchNumber = touchnum
firstTouchX = x
firstTouchY = y
triggered = False
If triggerDistance <= 0 Then
triggered = True
stickX = x-ringX
stickY = ringY-y
End
UpdateStick()
End
End
End
' a touch just moved, so we may need to update the stick
Method UpdateTouch:Void(x:Float, y:Float)
If touchNumber>=0 Then
If Not triggered Then
If (x-firstTouchX)*(x-firstTouchX)+(y-firstTouchY)*(y-firstTouchY) > triggerDistance*triggerDistance Then
triggered = True
End
End
If triggered Then
stickX = x - ringX
stickY = ringY - y
UpdateStick()
End
End
End
' we just released a touch, which may have been this one
Method StopTouch:Void()
If touchNumber>=0 Then
touchNumber = -1
stickX = 0
stickY = 0
stickAngle = 0
stickPower = 0
triggered = False
End
End
Method DoRenderRing:Void()
RenderRing(ringX, ringY)
End
Method DoRenderStick:Void()
RenderStick(ringX+stickX, ringY-stickY)
End
' draws the stick (may be overridden to do images, etc.)
Method RenderStick:Void(x:Float, y:Float)
DrawCircle(x, y, stickRadius)
End
' draws the outside ring (may be overridden to do images, etc.)
Method RenderRing:Void(x:Float, y:Float)
DrawCircle(x, y, ringRadius)
End
' set the location and radius of the ring
Method SetRing:Void(ringX:Float, ringY:Float, ringRadius:Float)
Self.ringX = ringX
Self.ringY = ringY
Self.ringRadius = ringRadius
End
' set the location and radius of the stick
Method SetStick:Void(stickX:Float, stickY:Float, stickRadius:Float)
Self.stickX = stickX
Self.stickY = stickY
Self.stickRadius = stickRadius
End
Method SetDeadZone:Void(deadZone:Float)
Self.deadZone = deadZone
End
Method SetTriggerDistance:Void(triggerDistance:Float)
Self.triggerDistance = triggerDistance
End
End
Function Main:Int()
New VirtualStickTestApp
Return 0
End
Function DrawOutlineRect:Void(x:Float, y:Float, width:Float, height:Float)
DrawLine(x, y, x+width, y)
DrawLine(x, y, x, y+height)
DrawLine(x+width, y, x+width, y+height)
DrawLine(x, y+height, x+width, y+height)
End