Archiv der Kategorie: Monkey X

Games: Flappy Farm für iOS

Ich hatte Lust ein weiteres Spiel das mit Monkey X programmiert wurde vorzustellen. Vielleicht hilft es ja diese Programmiersprache etwas populärer zu machen.

Wer kennt nicht die Erfolgsgeschichte von Flappy Bird und all den mehr oder weniger guten Kopien. Es ist schon faszinierend wie eine so simple Idee so einschlagen konnte. Teilweise mit einem abartigen Schwierigkeitsgrad und lustloser Umsetzung wurden die App Stores geflutet.

Ein Version die aus dieser Maße positiv heraus sticht ist Flappy Farm! Der Schwierigkeitsgrad ist angemessen aber nicht demotivierend. Das Spiel ist auf lange Motivation ausgelegt, daß sieht man an den Leveln die durch gespielt werden müssen.

Die Musik geht gut ins Ohr und auch an den Grafiken erkennt man dass mehr Mühe in dieses Spiel investiert wurde. Reinschauen lohnt sich!

[slideshow_deploy id=’1362′]

Games: Bloo Kid 2 für iOS

blookid2-287Ich dachte nicht dass ich irgendwann einmal einen Bericht über ein Spiel schreiben würde. Aber es sind einige Fakten zusammen gekommen die mich dazu bewegt haben. Zu aller erst bin ich ein großer Fan von Jump’n’Run Spielen. Indiegames finde ich immer sehr bemerkenswert und außerdem bin ich ein Fan der Monkey X Programmiersprache.

Die guten alten Amiga Zeiten sind schon lange vorbei, Spiele wie Turrican, Brian the Lion, Rick Dangerous und wie sie alles hießen haben mich in ihren Bann gezogen. Im Monkey X Forum bin ich auf Bloo Kid 2 aufmerksam geworden. Es handelt sich dabei um eine Indie Entwicklung, sprich wurde von einer einzigen Person realisiert und das schließt Grafik, Sound und Programmierung ein!

Alles ist im feinsten Retrostyle liebevoll gezeichnet und auch dem Sound merkt man diesen an. Das Gameplay ist ein klassischer Platformer. Die Touch-Steuerung ist sehr präzise und lässt keine realen Knöpfe vermissen. In allen Levels gibts es mehrere Ziele die es zu erreichen gilt.  Zeitlimit, alle Sterne, alle blauen Sterne, usw. für Spielspaß ist also gesorgt. 100% Gamer werden hier Ihre wahre Freude haben. Der Schwierigkeitsgrad steigt nach dem ersten Endgegner deutlich an.

An vielen Stellen merkt man das ausgeklügelte Leveldesign. Manche Secrets sind nicht so ohne weiteres zu erreichen und sind erst nach einem Sprung auf einen Gegner erreichbar. Alles in allem einer der, wenn nicht der beste Platformer den den ich im iTunes App Store gesehen habe. Das Spiel ist kostenlos und kann mit einem InApp Purchase werbefrei geschaltet werden. Und das Geld ist es alle mal Wert!

Und dank der multiplattform Fähigkeiten von Monkey X denke ich dass Bloo Kid 2 bald auch in anderen App Stores erscheinen wird.

[slideshow_deploy id=’1341′]

Monkey-X: AdMob für iOS und Android Apps

Monkey X DokumentationNach längerer Zeit wollte ich meine in Monkey X entwickelte App BuChao mit dem neuen iOS 7 SDK auf den aktuellen Stand bringen. Damals musste ich AdMob noch von Hand in das Xcode Projekt integrieren.

Natürlich sollte auch eine aktualisierte AdMob Version Verwendung finden. Die aktuelle Version war schon runter geladen und ich war mitten drin dieses wieder manuell einzubinden.

Doch irgendwo im Hinterstübchen konnte ich mich daran erinnern dass in Monkey X AdMob integriert wurde. Also ab ins Forum und die Suche bemüht und nichts gefunden? Auch Google lieferte irgendwie kein befriedigendes Ergebnis ;-( Unter „Monkey X AdMob Tutorial“, weder unter „Monkey X AdMob Example“ war irgend etwas zu finden…?

In solchen Fällen ist man immer am Zweifeln, ob man selbst zu blöd ist etwas zu finden oder ob andere so schlau sind dass keine Fragen aufkommen und deswegen nirgends etwas zu finden ist.

Die Lösung war aber so einfach und trivial das man sich auf die Stirn klatschen könnte. Der gute alte Ted (der Editor von Monkey) bietet eine Suchfunktion in der Monkey X Dokumentation und in Sekundenschnelle war die Doku samt Beispiel gefunden.

So, und jetzt habe ich extra einen Blogpost samt Sourcecode geschrieben damit Google auch was zum indizieren findet, damit der nächste Monkey X Entwickler schneller fündig wird als ich ;-)

#If TARGET<>"android" And TARGET<>"ios"
#Error "Admob is only supported on Android and iOS targets"
#End

#ADMOB_PUBLISHER_ID="abcdabcdabcdabc"                           'from your admob account
#ADMOB_ANDROID_TEST_DEVICE1="TEST_EMULATOR"
#ADMOB_ANDROID_TEST_DEVICE2="ABCDABCDABCDABCDABCDABCDABCDABCD"  'your device's admob ID for test mode

Import mojo
Import brl.admob

Class MyApp Extends App

    Field admob:Admob
    Field layout:=1
    Field enabled:=True

    Method OnCreate()
        admob=Admob.GetAdmob()
        admob.ShowAdView 1,layout
        SetUpdateRate 60
    End

    Method OnUpdate()
        If MouseHit( 0 )
            If enabled
                admob.HideAdView
                enabled=False
            Else
                layout+=1
                If layout=7 layout=1
                admob.ShowAdView 1,layout
                enabled=True
            Endif
        End
    End

    Method OnRender()
        Cls
        DrawText "Click to toggle ads!",DeviceWidth/2,DeviceHeight/2,.5,.5
    End

End

Function Main()
    New MyApp
End

 

Monkey: Debugging im GLFW Target mit Monkey

Monkey ist eine geniale Programmiersprache. Doch vor einiger Zeit bei meinem zweiten Monkey Projekt, in dem ich an irgendeiner Stelle eine nicht auffindbare Endlosschleife eingebaut habe, ist mir der Spaß ein wenig vergangen und ich habe Monkey bei Seite gelegt.

Und die Lösung war so nah, aber ich habe diese nicht erkannt obwohl diese vor meiner Nase lag. Letzthin als ich diesem Fehler wieder auf den Grund gehen wollte fiel es mir wie Schuppen von den Augen ;-)

monkey-glfw-debugging

Im GLFW und C++ Target kann nämlich der Debugger genutzt werden! Da ich aber immer im HTML5 Target getestet habe ist mir das nie richtig aufgefallen. Vor dem kompilieren im Editor muß der Modus von Release auf Debug umgestellt werden. Der Debugger selbst wird durch den Befehl „DebugStop“ im Quellcode aktiviert. Dabei wird die Ausführung des Programmes gestoppt. Im Anschluss können im Ted Debug Fenster alle Objekte und Variablenwerte inspiziert werden.

Damit werde ich hoffentlich bald dem Fehler auf die Schliche kommen und mein Projekt weiter voran treiben…

Monkey: Target Android

Monkey ist einfach genial. Nach erfolgreichen probieren der HTML5 und iOS Targets wollte ich mal etwas über den Tellerrand zu Android schauen.

Ein Android Target mit Monkey zu erzeugen ist ja kein Problem. Aber was wird alles benötigt um für Android zu entwickeln? Zu aller erst organisiert man sich das Android SDK für die entsprechende Plattform. In meinem Fall war es für Mac OS X (intel).

So weit so gut, was kommt als nächstes. Unter OS X muss in dem Android SDK Ordner „tools“ das Kommandozeilenprogramm „android“ gestartet werden. Es erscheint dann der „Android SDK and AVD Manager“. Nun lädtman sich im nächsten Schritt die gewünschte Android SDK Platform.

Ist das erledigt ist es ratsam unter „Virtual devices“ ein AVD anzulegen. Dieses stellt dann einen Android Simulator zur Verfügung. Die ersten Weichen sind gestellt.

Monkey & Netbeans

Das SDK ist eingerichtet, Monkey hat ein Android Target Projekt erzeugt, aber was nun? Nach ein wenig suche, wenn man die bekannten Java IDE’s abklappert, wird man bei Netbeans fündig (wenn ich ehrlich bin habe ich nur nach Netbeans gesucht).

Für die Netbeans IDE gibt es das „nbandroid“ Plugin. Dieses steht nicht selbst über das Netbeans Repository zur Verfügung. Um dieses zu installieren muss im „Plugins Fenster“ unter „Settings“ eine weitere Update Center Quelle angelegt werden. Dies wird hier sehr gut beschrieben.

Nach erfolgreicher installation kann man direkt aus Netbeans heraus Android Projekte erzeugen/starten, compilieren, usw.

Monkey hat dies aber schon erledigt und Netbeans muss nicht mehr dazu bemüht werden. Netbeans wird jetzt dazu gebraucht das Android Target zu kompilieren und entweder auf ein echtes oder auf ein simuliertes Device zu kopieren.

Und schon steckt man ganz tief in der Welt von Android und Java ;o) Bei meinen ersten Versuchen war ich bis jetzt noch nicht erfolgreich mein Monkey Projekt unter Android zum laufen zu bewegen, snif.

Monkey: Target iOS

Exportiert man auf die iOS Plattform wird über ein Template ein Xcode 3 Projekt erzeugt. Dieses kann auch ohne weiteres mit Xcode 4 geöffnet werden.

Im Moment wird das Projekt mit dem Namen „MonkeyGame“ erzeugt. Dieser kann angeblich über eine Config Datei eingestellt werden. Da alles aber über Xcode selbst eingestellt werden kann habe ich mich auch noch nicht richtig auf die Suche gemacht ;o)

In Xcode müssen/können noch kleine Änderungen wie Default Screen, zu verwendende Icons sowie die Device Orientation eingestellt werden. Auch ob die Titlebar angezeigt werden soll oder nicht kann nachtröglich noch geöndert werden.

Tiefergreifende Änderungen können natürlich noch in Xcode vorgenommen werden. Der bessere Weg wäre natürlich weitere iOS Funktionen als Modul Monkey zur Verfügung zu stellen.

Monkey: Target HTML5

In der Monkey Demoversion ist HTML5 die einzige Zielplattform die ausgewählt werden kann. Erst in der Vollversion stehen alle andere Plattformen zur Verfügung.

HTML5 ist unter Monkey die schnellste Zielplattform auf der man seine Apps ausprobieren kann. Diese braucht keinen Simulator und muss auch nicht kompiliert werden. Schlichtweg ein möglichst modernen Browser mit guter HTML5 Unterstützung reicht hier aus.

Google Chrome liegt sehr hoch in der Gunst der Monkey Entwickler. Safari und Firefox stehen aber Chrome sicher in nichts nach.

Monk startet komischerweise einen eigenen lokalen Webserver um die HTML5 App zu starten. Erfahrungsgemäß läuft dieser aber, zumindest bei mir, nicht sehr stabil und stürzt gerne hin und wieder ab.

Dieser ist nicht unbedingt notwendig. Die erzeugte HTML Datei kann logischerweise auch direkt mit dem Browser geöffnet werden.

Update: Ein direktes öffnen ist mittlerweile nicht mehr möglich. Das HTML5 Target benötigt den lokalen Webserver.

Mehr gibt es eigentlich hier nicht zu berichten. Die erzeugten Dateien können so wie diese sind auf eine bestehende Homepage übernommen werden.

TYPO3: Extension t3monkey

Irgendwie wollte ich meine Begeisterung von Monkey und TYPO3 [die für T3 ist mittlerweile am abklingen ;-)] zusammen bringen und habe eine kleine Extension für TYPO3 entwickelt, mit der man das HTML5 Target von Monkey über ein Plugin in die TYPO3 Seite integrieren kann.

t3monkey-demo-config

Einfach den Build Ordner von Monkey in den fileadmin hoch laden, das Plugin auf einer beliebigen Seite einfügen und dann in der Plugin Flexform auf diesen Ordner verweisen => fertig!

t3monkey-demo

Nur leider musste ich feststellen das meine Extension einer Aufräumaktion zum Opfer gefallen war und diese nun nicht mehr im TYPO3 Repository zu finden ist. Warum auch immer? Einfach so ohne Kommentar, das hat mich ein bisschen geärgert! Und deswegen werde ich mir auch nicht mehr die Mühe machen diese ins TER hoch zu laden.

Trotzdem will ich aber den letzten Stand auf meiner Seite zur Verfügung stellen! Diese liegt nun „nur“ als zip-Datei vor, da das neue TYPO3 6 aus dem Extensionmanager „nur“ noch zip-Files und keine t3x-Files mehr exportiert..?

[wpdm_file id=13]

Monkey: asteroids game example

Strict

Import mojo

Global game:MyGame

Function Main:Int()
    game = New MyGame
    Return 0
End Function

Const PDM_smlparticle%=0
Const PDM_medparticle%=1
Const PDM_bigparticle%=2
Const PDM_spark%=3

Global ASTEROIDS_NUM% = 6
Global ASTEROIDS_SIZE% = 4

' Store the device width and height
Global SCREEN_WIDTH%
Global SCREEN_HEIGHT%
' Half of SCREEN_WIDTH and HEIGHT
Global SCREEN_WIDTH2%
Global SCREEN_HEIGHT2%

' Used for delta timing movement
Global dt:DeltaTimer

' Screen states
Const STATE_TITLE% = 1
Const STATE_GAME% = 2
Const STATE_GAME_OVER% = 3

Class MyGame Extends App

Field player:Player
Field cg%, cr%, cb%
Field level% = 1
Field score% = 0
Field bestScore% = 0

Field FPS% = 60
' Current game state
Global gameState:Int = STATE_TITLE

Method OnCreate:Int()
    ' Store the device width and height
    SCREEN_WIDTH = DeviceWidth()
    SCREEN_HEIGHT = DeviceHeight()
    SCREEN_WIDTH2 = SCREEN_WIDTH / 2
    SCREEN_HEIGHT2 = SCREEN_HEIGHT / 2
    ' Set the Random seed
    Seed = Millisecs()
    ' Create the delta timer
    dt = New DeltaTimer(FPS)
    SetUpdateRate FPS

    reset()
    Return 0   
End Method

Method OnLoading:Int()
    Cls 0,0,0
    DrawText("Loading", SCREEN_WIDTH2, SCREEN_HEIGHT2, 0.5, 0)
    Return 0
End Method

Method OnUpdate:Int()
    FPSCounter.update()
    dt.UpdateDelta()

    Select gameState
    Case STATE_TITLE
        Asteroid.updateAll()
        If KeyHit(KEY_SPACE)
           setState(STATE_GAME)
        Endif   
    Case STATE_GAME
        player.Update()
        Bullet.updateAll()
        Asteroid.updateAll()
        Particle.updateAll()
        checkCollisions()
        clscolor()
    Case STATE_GAME_OVER
        Asteroid.updateAll()
        Particle.updateAll()   
        If KeyHit(KEY_SPACE)
           setState(STATE_TITLE)
            reset()
        Endif
    End Select

    Return 0
End Method

Method OnRender:Int()
    Select gameState
    Case STATE_TITLE
    Cls 0, 0, 0
    Asteroid.drawAll()
    DrawText ("ASTEROIDS - MONKEY STYLE!", SCREEN_WIDTH2, SCREEN_HEIGHT2, 0.5, 0.5)
    DrawText ("BEST SCORE: "+Self.bestScore, SCREEN_WIDTH2, SCREEN_HEIGHT2 + 30, 0.5, 0.5)
    DrawText ("PRESS <space> TO PLAY", SCREEN_WIDTH2, SCREEN_HEIGHT2 + 60, 0.5, 0.5)
    Case STATE_GAME 
    Cls cr, cg, cb
    player.draw()
    Bullet.drawAll()
    Asteroid.drawAll()
    Particle.drawAll()
    drawHUD()
    Case STATE_GAME_OVER
    Cls 0, 0, 0
    Asteroid.drawAll()
    Particle.drawAll()
    DrawText ("GAME OVER!", SCREEN_WIDTH2, SCREEN_HEIGHT2, 0.5, 0.5)
    DrawText ("SCORE: "+score, SCREEN_WIDTH2, SCREEN_HEIGHT2 + 30, 0.5, 0.5)
    DrawText ("BEST SCORE: "+Self.bestScore, SCREEN_WIDTH2, SCREEN_HEIGHT2 + 60, 0.5, 0.5)
    DrawText ("PRESS <space> TO RETURN TO THE TITLE SCREEN", SCREEN_WIDTH2, SCREEN_HEIGHT2 + 90, 0.5, 0.5)
    End Select
    Return 0
End Method

Method setState:Void(state:Int)
    gameState = state
End

Method clscolor:Void()
    If cr > 0
    cr-=2*dt.delta
    Else
    cr = 0
    Endif
End Method

Method reset:Void()
    Bullet.list.Clear()
    Asteroid.list.Clear()
    Particle.list.Clear()
    cg = 0
    cr = 0
    cb = 0
    level = 1
    ASTEROIDS_NUM = 6
    ASTEROIDS_SIZE = 4
    score = 0
    player = New Player(SCREEN_WIDTH2, SCREEN_HEIGHT2)
    fillAsteroids(ASTEROIDS_NUM, ASTEROIDS_SIZE)   
End Method

Method checkCollisions:Void()
    For Local a:Asteroid = Eachin Asteroid.list

        If dist(player.x, player.y, a.x, a.y) <= a.avgrad
        If cr = 0 Then cr = Rnd(100, 155)
        player.shield-=2
        Endif

        For Local b:Bullet = Eachin Bullet.list 
            If a <> Null Then
                If dist(b.x, b.y, a.x, a.y) <= a.avgrad
                    a.life = a.life - 1
                    b.life = 0
                    For Local t%=1 To 4
                        New Particle(a.x, a.y, Rnd(-8,8), Rnd(-8,8), 0.95, 30, PDM_spark, 255, 192, 64, 16)
                    Next
                    For Local t%=1 To 4
                        New Particle(a.x, a.y, Rnd(-4,4), Rnd(-4,4), 0.95, 60, PDM_smlparticle, 160, 160, 160, 0)
                    Next
                Endif
                If a.life <= 0

                For Local t%=1 To 8
                    New Particle(a.x,a.y,Rnd(-10,10),Rnd(-10,10),0.95,30,PDM_spark,255,192,64,64)
                Next
                For Local t%=1 To 6
                    New Particle(a.x,a.y,Rnd(-6,6),Rnd(-6,6),0.95,30,PDM_medparticle,255,192,64,128)
                Next
                For Local t%=1 To 6
                    New Particle(a.x,a.y,Rnd(-8,8),Rnd(-8,8),0.99,60,PDM_smlparticle,160,160,160,0)
                Next
                For Local t%=1 To 5
                    New Particle(a.x,a.y,Rnd(-6,6),Rnd(-6,6),0.99,60,PDM_medparticle,160,160,160,0)
                Next
                For Local t%=1 To 4
                    New Particle(a.x,a.y,Rnd(-4,4),Rnd(-4,4),0.99,60,PDM_bigparticle,160,160,160,0)
                Next

                If a.size > 1
                    For Local t% = 1 To 2
                        New Asteroid(a.x, a.y, Rnd(-5,5), Rnd(-5,5), a.size-1)
                    Next
                Endif
                Asteroid.list.Remove(a)
                a = Null
                score+=5
                Endif
            Endif
        Next
    Next
    If Asteroid.list.Count() = 0
        level+=1
        ASTEROIDS_SIZE+=1
        ASTEROIDS_NUM+=1
        fillAsteroids(ASTEROIDS_NUM, ASTEROIDS_SIZE)
    Endif
End Method

    Method drawHUD:Void()
        DrawText ("LEVEL: "+level, 0, 0)
        DrawText("SCORE: "+score, SCREEN_WIDTH, 0, 1, 0)

        FPSCounter.draw(SCREEN_WIDTH,SCREEN_HEIGHT, 1, 1)
    End Method

    Method fillAsteroids:Void(num%, size%)
        Local tx#
        Local ty#
        For Local t% = 1 To num
            Repeat
                tx=Rnd(640)
                ty=Rnd(480)
            Until ( tx<280 or="" tx="">360 ) And ( ty<200 or="" ty="">280 )
            New Asteroid(tx, ty, Rnd(-3,3), Rnd(-3,3), size+Rnd(1))
        Next
    End Method
End Class

Class Sprite
    Field xv# ,yv#
    Field x#, y# 
    Field rotation#
End Class

Class Player Extends Sprite
    Field angVel#
    Field velmul#
    Field vel#
    Field acc#
    Field drag#
    'Field xv#,yv#
    Field xa#,ya#
    Field firedel#

    Field ship_angvel#
    Field ship_acc#
    Field ship_velmul#
    Field ship_firedel#

    Field shield#=100

    Method New(x#, y#)
        Self.x = x
        Self.y = y
        ship_angvel = 6
        ship_acc = 0.16
        ship_velmul = -0.0005
        ship_firedel = 4
        shield = 100
    End Method

    Method Update:Void()

        If KeyDown(KEY_UP)
            acc =  ship_acc
            drag = vel * ship_velmul
        Else If KeyDown(KEY_DOWN)
            drag = vel * ship_velmul * 50
        Else
            acc = 0
            drag = 0
        End If 
        If KeyDown(KEY_LEFT)
            rotation+=ship_angvel * dt.delta
        End If
        If KeyDown(KEY_RIGHT)
            rotation-=ship_angvel * dt.delta
        End If

        If KeyDown(KEY_SPACE) And Self.firedel<=0
            Local tang#=Rnd(-4,4)
            New Bullet(x - (Sin(rotation)*8), y-(Cos(rotation)*8), xv - (Sin(rotation + tang ) *12), yv-(Cos(rotation + tang ) *12), 45, 255-Rnd(4), 192+Rnd(-4,4), 64+Rnd(4,4))
            firedel = ship_firedel
        Endif
        firedel-=dt.delta

        xa = (drag * xv) - (Sin(rotation) * acc)
        ya = (drag * yv) - (Cos(rotation) * acc)
        xv = xv + xa *dt.delta
        yv = yv + ya * dt.delta
        x = x + xv * dt.delta
        y = y + yv * dt.delta
        vel = dist(0, 0, xv, yv)
        '   
        If x < 0 x = SCREEN_WIDTH
        If x > SCREEN_WIDTH x = 0
        If y < 0 y = SCREEN_HEIGHT
        If y > SCREEN_HEIGHT y = 0

        If shield <= 0
        For Local t%=1 To 18
            New Particle( x, y,Rnd(-10,10),Rnd(-10,10),0.95,130,PDM_spark,255,192,64,64)
        Next
        For Local t%=1 To 16
            New Particle( x, y,Rnd(-6,6),Rnd(-6,6),0.95,130,PDM_medparticle,255,192,64,128)
        Next
        For Local t%=1 To 16
            New Particle( x, y,Rnd(-8,8),Rnd(-8,8),0.99,160,PDM_smlparticle,160,160,160,0)
        Next
        For Local t%=1 To 15
            New Particle( x, y,Rnd(-6,6),Rnd(-6,6),0.99,160,PDM_medparticle,160,160,160,0)
        Next
        For Local t%=1 To 14
            New Particle( x, y,Rnd(-4,4),Rnd(-4,4),0.99,160,PDM_bigparticle,160,160,160,0)
        Next

        If game.score>game.bestScore
            game.bestScore = game.score
        Endif

        game.setState(STATE_GAME_OVER)
        Endif
    End Method

    Method draw:Void()
        Local x1# = x-(Sin(rotation) * 10)
        Local y1# = y-(Cos(rotation) * 10)
        Local x2# = x-(Sin(rotation + 140 ) * 8)
        Local y2# = y-(Cos(rotation + 140 ) * 8)
        Local x3# = x-(Sin(rotation - 140 ) * 8)
        Local y3# = y-(Cos(rotation - 140 ) * 8)
        SetColor 255, 255, 255
        DrawLine x1, y1, x2, y2
        DrawLine x2, y2, x3, y3
        DrawLine x3, y3, x1, y1

        SetAlpha 0.5
        If shield < 50 Then SetColor 255,0,0
        DrawRect 10,SCREEN_HEIGHT - 15, Self.shield, 10
        SetAlpha 1
        SetColor 255,0,0
    End Method

End Class

Class Bullet Extends Sprite
    Global list:List<bullet> = New List<bullet>

    Field life#
    Field cr%, cg%, cb%

    Method New(x#,y#,xv#,yv#,life#,cr%,cg%,cb%)
        Self.x = x
        Self.y = y
        Self.xv = xv
        Self.yv = yv
        Self.life = life
        Self.cr = cr
        Self.cg = cg
        Self.cb = cb

        list.AddLast Self
    End Method

    Function updateAll:Void()
        If Not list Return
        For Local b:Bullet = Eachin list
            b.update()
            If b.life < 0
                Bullet.list.Remove(b)
            b = Null
        Endif

        Next
    End Function

    Method update:Void()
        x = x + xv * dt.delta
        y = y + yv * dt.delta

        life-=dt.delta
        If x < 0 x = SCREEN_WIDTH
        If x > SCREEN_WIDTH x = 0
        If y < 0 y = SCREEN_HEIGHT
        If y > SCREEN_HEIGHT y = 0
    End Method

    Function drawAll:Void()
        If Not list Return
        For Local b:Bullet = Eachin list
            b.draw()
        Next   
    End Function

    Method draw:Void()
    Local tmul#
    If life <= 15.0
    tmul = life / 15.0
    Else
    tmul = 1.0
    Endif
    SetColor cr*tmul, cg*tmul, cb*tmul
    DrawLine x, y, x + xv, y + yv
    End Method
End Class

Class Asteroid Extends Sprite
    Global list:List<asteroid> = New List<asteroid>

    Field ang#,angvel#
    Field rad#[9]
    Field avgrad#
    Field size%
    Field life%
    Field cr%, cg%, cb% 

    Method New(x#,y#,xv#,yv#,size%)
        Self.x =x 
        Self.y =y 
        Self.xv =xv 
        Self.yv =yv 
        Self.ang =Rnd(360)
        Self.angvel =Rnd(-6,6)
        Self.size=size
        Self.life=size
        Local tcol% = Rnd(-48,48)
        Self.cr=128+tcol
        Self.cg=128+tcol
        Self.cb=128+tcol
        ' Create "Rockiness"
        Self.avgrad =0
        For Local t% = 0 To 7
            Self.rad [t]=size*8.0+Rnd(-size*4.0,size*4.0)
            Self.avgrad =Self.avgrad +Self.rad[t]
        Next
        Self.avgrad =Self.avgrad /6.0
        Self.rad[8] = Self.rad[0]

        list.AddLast Self
    End Method

    Function drawAll:Void()
        If Not list Return
            For Local b:Asteroid = Eachin list
            b.draw()
        Next   
    End Function

    Function updateAll:Void()
        If Not list Return
            For Local b:Asteroid = Eachin list
            b.update() 
        Next
    End Function

    Method update:Void()
        Self.x =Self.x +Self.xv * dt.delta 
        Self.y =Self.y +Self.yv * dt.delta 
        Self.rotation =Self.rotation +Self.angvel * dt.delta 

        If Self.x <-Self.avgrad  Then Self.x =Self.x + SCREEN_WIDTH + Self.avgrad *2
        If Self.x >SCREEN_WIDTH+Self.avgrad  Then Self.x =Self.x - SCREEN_WIDTH - Self.avgrad *2
        If Self.y <-Self.avgrad  Then Self.y =Self.y + SCREEN_HEIGHT + Self.avgrad *2
        If Self.y >SCREEN_HEIGHT+Self.avgrad  Then Self.y =Self.y - SCREEN_HEIGHT - Self.avgrad *2
    End Method

    Method draw:Void()
        Local tmul# = 360.0 / 8.0
        SetColor cr, cg, cb
        For Local t% = 0 To 7
            DrawLine x-(Sin(rotation+(t)*tmul)*rad[t]),y-(Cos(rotation+(t)*tmul)*rad[t]),x-(Sin(rotation+(t+1)*tmul)*rad[t+1]),y-(Cos(rotation+(t+1)*tmul)*rad[t+1])
        Next
    End Method
End Class

Class Particle Extends Sprite
    Global list:List<particle> = New List<particle>

    Field vm#
    Field life#,mlife#
    Field drawmode%
    Field cr%,cg%,cb%
    Field cflash%

    Method New(x#,y#,xv#,yv#,vm#,life#,drawmode%,cr%,cg%,cb%,cflash%)
        Self.x=x
        Self.y=y
        Self.xv=xv
        Self.yv=yv
        Self.vm=vm
        Self.life=life
        Self.mlife=life
        Self.drawmode=drawmode
        Self.cr=cr
        Self.cg=cg
        Self.cb=cb
        Self.cflash=cflash

        list.AddLast Self
    End Method

    Function updateAll:Void()
        If Not list Return
        For Local b:Particle = Eachin list
            b.update() 
        Next
    End Function

    Function drawAll:Void()
        If Not list Return
        For Local b:Particle = Eachin list
            b.draw()
        Next   
        End Function

    Method update:Void()
        Self.x =Self.x +Self.xv *dt.delta
        Self.y =Self.y +Self.yv *dt.delta
        Self.xv =Self.xv *(1.0-(1.0-Self.vm )*dt.delta )
        Self.yv =Self.yv *(1.0-(1.0-Self.vm )*dt.delta )
        Self.life =Self.life -dt.delta
        If Self.life <0 then="" selflife="0" if="" listremoveself="" endif="" end="" method="" draw:void="" local="" tmul="Self.life" selfmlife="" tfls="Rnd(-Self.cflash,Self.cflash)" setcolor="" limit="" selfcrtmul="" tfls0255limit="" selfcgtmul="" selfcb="" tmultfls0255="" select="" selfdrawmode="" case="" pdm_smlparticle="" drawrect="" selfx="" selfy="" 1="" pdm_medparticle="" drawoval="" -1selfy="" -133="" pdm_bigparticle="" -2selfy="" -255="" pdm_spark="" drawline="" selfxv="" selfyv="" class="" fpscounter="" abstract="" global="" fpscount:int="" starttime:int="" totalfps:int="" function="" update:void="" millisecs="" -="" starttime="">= 1000
            totalFPS = fpsCount
            fpsCount = 0
            startTime = Millisecs()
        Else
            fpsCount+=1
        Endif
    End Function

    Function draw:Void(x% = 0, y% = 0, ax# = 0, ay# = 0)
        DrawText("FPS: " + totalFPS, x, y, ax, ay)
    End Function
End Class

' From James Boyd
Class DeltaTimer
    Field targetfps:Float = 60
    Field currentticks:Float
    Field lastticks:Float
    Field frametime:Float
    Field delta:Float

    Method New (fps:Float)
        targetfps = fps
        lastticks = Millisecs()
    End

    Method UpdateDelta:Void()
        currentticks = Millisecs()
        frametime = currentticks - lastticks
        delta = frametime / (1000.0 / targetfps)
        lastticks = currentticks
    End
End

Function dist#(x1#,y1#,x2#,y2#)
    Return Sqrt(Pow((x1-x2),2) + Pow((y1-y2),2))
End Function

Function limit#(value#,low#,high#)
    If value < low Then Return low
    If value > high Then Return high
    Return value
End Function
</particle></particle></asteroid></asteroid></bullet></bullet></space></space>

 

Monkey: virtual stick

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