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