blox'n'balls NG

Kommentare anzeigen Worklog abonnieren

Worklogs blox'n'balls NG

Mal etwas praktisches...

Donnerstag, 2. Februar 2012 von Farbfinsternis
Während ich mich zuletzt um Sachen gekümmert habe die dem Spiel nichts hinzufügen, habe ich mich jetzt um die Verbesserung der Steuerung gekümmert und ein wenig an den States innerhalb des Spiels gebastelt. So kann ich nun der Klasse "Ball" die maximale Anzahl der Bälle übergeben und die Methode Ball.update() gibt mir zurück ob alle Bälle verspielt sind oder nicht. Die verbleibende Anzahl der Bälle wird nun angezeigt und wenn kein Ball mehr verfügbar ist wird ein wahnsinnig schlechtes MP3 Sample abgespielt.

Das alles wird so nicht in bnb implementiert, es sind alles Sachen die das Framework auf die Sachen die da kommen vorbereiten. Wichtig war jetzt dass man mit dem Paddle den Ball exakter steuern kann und dass es möglich ist ein Spielende zu definieren.

Abschliessend noch ein Link zum aktuellen Status in Flash: http://www.sedm.de/monkey/bnb/flash/

Partikel

Dienstag, 27. Dezember 2011 von Farbfinsternis
Ein wichtiger Aspekt von b'n'b waren immer die Partikel. Ich habe deshalb nun zwei Klassen ("ParticleEmitter" und "Particle") implementiert um das in der NG Version fortzusetzen. Partikel werden stets über den Emitter gesteuert, es ist nicht anzuraten Methoden der Klasse "Particle" direkt zu verwenden.
So erzeugt man einen neuen Emitter ganz klassisch mit New() und übergibt ihm die ImageRef des ResourceManagers und die Position des Emitters. Zur Laufzeit darf die Position jederzeit mit der Methode "setPosition()" geändert werden.
Das nicht direkt ein Image, sondern ein Object vom Typ "ImageRef" übergeben wird hat den Grund dass die Targets "HTML5" und "Flash" nicht immer gleich alle Daten zur Verfügung haben. So muss man warten bis alle Bilder geladen wurden. Das Modul "class_particle.monkey" stellt somit selbst sicher dass erst etwas gezeichnet wird wenn alle Daten verfügbar sind.
Der Emitter erzeugt kontinuierlich Partikel. Das Verhalten der Partikel wird nach dem Erzeugen des Emitters festgelegt, kann aber zur Laufzeit geändert werden. Derzeit kann man die Beschleunigung (Velocity), die Streuung (Scattering) bestimmen sowie festlegen ob die Partikel in ihrer Lebenszeit ein- oder ausgeblendet werden sollen. Es fehlt noch die Möglichkeit die Partikel zu skalieren oder das Fading zu kombinieren (bspw. das Partikel eingeblendet und zu ihrem Lebensende wieder ausgeblendet werden). Zudem fehlt noch die Möglichkeit am Lebensende oder zu einer bestimmten Zeit neue Partikel zu erzeugen. Bspw. ein Feuer dessen Partikel am Lebensende zu Rauch werden etc.
Monkey zwingt uns leider dazu das Update und das Rendering der Partikel zu trennen, was bedeutet dass die Liste der Partikel doppelt iteriert werden muss. Außerdem ist es nicht möglich ein SingeSurface System zu programmieren weil einige Targets das nicht unterstützen (Mojo von Haus aus überhaupt nicht). So muss man aufpassen dass man nicht zuviele Partikel erzeugt. Aus diesem Grund versuche ich ein Tweening zu implementieren welches es ermöglicht mehr Partikel zu suggerieren als eigentlich zu sehen sind. Derzeit sind es einfach nur gleichgroße Images die positioniert und mit einem Alpha-Wert versehen werden.

Als Demo habe ich die letzte Demo verwendet und im iDS Screen einen Partikel-Effekt angewendet der am Mauszeiger hängt.

HTML5 Demo
Flash Demo

Als nächstes wird die Particle Klasse dann Gravitation, einen Boden (zum erneuten abprallen der Partikel) und Forces unterstützen. Desweiteren werde ich den ResourceManager und die Partikel Geschichte zu einem Modul kombinieren so dass das jeder Interessierte auch für seine Projekte nutzen kann.

States & Resources

Sonntag, 25. Dezember 2011 von Farbfinsternis
In Monkey hat man oft das Problem dass man nicht weiß ob schon alle Daten geladen sind die man benötigt, darum habe ich den ResourceManager von clevijoki so umgebaut dass er Strict ist und als Module verwendbar ist. Daneben habe ich den Statemanager implementiert der die richtige Klasse zur rechten Zeit aufruft. Jeder Zustand des Programms ist eine eigene Klasse (die einzelnen Logos des Intros, das Hauptmenü, das Spiel selbst und es kommen wohl noch ein paar dazu).
Der Flow sieht derzeit so aus: iDS Logo (2sec oder MouseHit/Touch) -> SEDM Logo (2sec oder MouseHit/Touch) -> bnb Logo (2sec oder MouseHit/Touch) -> MainMenu -> Game <- MainMenu -> Game etc.

Das MainMenu und der Game Screen sind noch ohne Funktion, der Switch ist ein einfaches MouseHit oder ein Touch.

Die Targets Android und GLFW sorgen noch ein bisschen für Ärger. Unter Android werden die Fade timings nicht eingehalten (kann daran liegen dass die Images in FullHD vorliegen) und unter GLFW gehts garnicht (kann daran liegen dass die Images nicht in einer Grösse ^2 vorliegen. Wird noch geklärt.

Hier könnt ihr die Demo für HTML5 anschauen.

Die Grafiken sind allesamt noch Platzhalter.

Neben diesem Gedöns konnte ich dank zahlreicher geduldiger helfender Elfen die Vector-Klasse vervollständigen. Box2D habe ich rausgeschmissen weil es einfach nicht die Performance liefert die ich mir wünsche. Nun wird die Kollision doch selbst implementiert weil es mir sehr wichtig ist dass es schnell ist und dass auch nicht rechteckige Levels möglich sind.

Soviel für heute *gähn* ... seid gespannt was als nächstes kommt Wink

Physik

Montag, 19. Dezember 2011 von Farbfinsternis
Im Moment bastle ich ein wenig mit Box2D herum da mir das BoxBox Kollisionssystem nicht gefällt und ich auch mal Levels erzeugen will die nicht rechteckig sind.
Herausgekommen ist ein kleines Textprogramm welches per Mausklick Kisten erzeugt welche dann physikalisch korrekt umher fliegen.
Code: [AUSKLAPPEN]
Strict

Import mojo
Import box2d.common
Import box2d.collision
Import box2d.dynamics

Function RadToDeg:Int(rad:Float)
   if rad <> 0.0 Then Return (rad * 180.0) / PI Else Return 0
End Function

Class CEntity
   Field bodyDef:b2BodyDef
   Field bodyShape:b2PolygonShape
   Field body:b2Body
   Field fixtureDef:b2FixtureDef
   Field img:Image
   
   Method CreateBox:Void(world:b2World, x:Float, y:Float, width:Float, height:Float, static:Bool = False)
      Self.fixtureDef   = New b2FixtureDef()
      Self.bodyShape   = New b2PolygonShape()
      Self.bodyDef   = New b2BodyDef()
      
      If static = True
         Self.bodyDef.type = b2Body.b2_staticBody
      Else
         Self.bodyDef.type = b2Body.b2_Body
      endif      
      
      Self.fixtureDef.density      = 1.0
      Self.fixtureDef.friction   = 0.3
      Self.fixtureDef.restitution   = 0.1
      Self.fixtureDef.shape      = Self.bodyShape
      Self.bodyDef.position.Set(x, y)
      Self.bodyShape.SetAsBox(width, height)
      Self.body = world.CreateBody(Self.bodyDef)
      Self.body.CreateFixture(Self.fixtureDef)
      Self.bodyDef.allowSleep      = True
      Self.bodyDef.awake         = True
   End
   
   Method CreateBox:Void(world:b2World, img:Image, x:Float, y:Float, static:Bool = False)
      Self.img = img
      Self.img.SetHandle(Self.img.Width() / 2, Self.img.Height() / 2)
      Self.CreateBox(world, x, y, img.Width() / 2, img.Height() / 2, static)
   End
   
   Method SetFriction:Void(friction:Float = 0.5)
      Self.fixtureDef.friction = friction
      Self.body.CreateFixture(Self.fixtureDef)
   End
   
   Method SetDensity:Void(density:Float = 1.0)
      Self.fixtureDef.density = density
      Self.body.CreateFixture(Self.fixtureDef)
   End
   
   Method SetRestitution:Void(restitution:Float = 0.1)
      Self.fixtureDef.restitution = restitution
      Self.body.CreateFixture(Self.fixtureDef)
   End
   
   Method SetMass:Void(mass:Float)
      Local md:b2MassData = new b2MassData()
      Self.body.GetMassData(md)
      md.mass = mass
      Self.body.SetMassData(md)
   End
   
   Method SetImage:Void(img:Image)
      Self.img = img
   End
   
   Method Draw:Void(ratio:Float = 1.0)
      if Self.img <> Null
         Local x:Float   = self.body.GetPosition().x
         Local y:Float   = self.body.GetPosition().y
         Local r:Float   = RadToDeg(Self.body.GetAngle()) * -1
         
         DrawImage(Self.img, x, y, r, 1.0, 1.0, 0)
      EndIf
   End
End

Class CWorld
   Field world:b2World
   Field m_velocityIterations:int
    Field m_positionIterations:int
    Field m_timeStep:Float
   Field ratio:Float
   
   Field entities:List<CEntity>
   
   Method New(ratio:Float = 1.0, gravityX:Float = 0.0, gravityY:Float = 10.0)
      Self.ratio = ratio
      Self.world = New b2World(New b2Vec2(gravityX, gravityY), True)   
      Self.world.SetGravity(new b2Vec2(gravityX, gravityY))
      
      Self.m_velocityIterations   = 3
      Self.m_positionIterations   = 3
      Self.m_timeStep            = 1.0 / 10.0
      
      Self.entities = New List<CEntity>()
   End
   
   Method update:Void()
      Self.world.TimeStep(self.m_timeStep, self.m_velocityIterations, self.m_positionIterations)
      Self.world.ClearForces()
   End
   
   Method render:Void()
      if Self.entities.Count() > 0
         For Local e:CEntity = eachin self.entities
            if e <> Null e.Draw(self.ratio)
         Next            
      EndIf
   End
   
   Method CreateBox:CEntity(x:Float, y:Float, width:Float, height:Float, static:Bool = false)
      Local entity:CEntity = new CEntity()
      entity.CreateBox(Self.world, x, y, width, height, static)
      Self.entities.AddLast(entity)
      
      Return entity
   End
   
   Method CreateBox:CEntity(img:Image, x:Float, y:Float, static:Bool = False)
      Local entity:CEntity = new CEntity()
      entity.CreateBox(Self.world, img, x, y, static)
      Self.entities.AddLast(entity)
      
      Return entity
   End
End

Class CTest extends App
   Field world:CWorld
   Field player:Image
   Field playerEntity:CEntity
   Field groundEntity:CEntity
   Field ratio:Float
   
   Method OnCreate:Int()
      Self.ratio         = 640.0 / DeviceWidth()
      Self.world          = New CWorld(Self.ratio)
      Self.player         = LoadImage("player.png")
      Self.groundEntity   = Self.world.CreateBox(0, DeviceHeight() - 10, DeviceWidth(), 10, True)
      
      SetUpdateRate(60)
      
      Return 0
   End
   
   Method OnUpdate:Int()
      Self.world.update()
      
      if MouseHit() Or TouchHit()
         Local mx:Float   = MouseX()
         Local my:Float   = MouseY()
         Local e:CEntity = self.world.CreateBox(self.player, mx, my)
         e.body.SetAngularVelocity(Rnd(-0.1, 0.1))
         
      EndIf
      Return 0
   End
   
   Method OnRender:Int()
      Cls(90, 120, 200)
      #if TARGET = "android"
         DrawText("touch to spwan a new crate", DeviceWidth() / 2, 20, 0.5)
      #Else
         DrawText("left click to spwan a new crate", DeviceWidth() / 2, 20, 0.5)
      #EndIf
      
      Self.world.render()
      
      Return 0
   End
End

Function Main:Int()
   New CTest()
   Return 0
End


user posted image

Das kleine Testprogramm hat auch gleich die Idee mitgebracht Box2D noch ein klein wenig mehr zu wrappen um es benutzerfreundlicher zu machen.

Flash Version
Die Flash Version zeigt einen QR Code für die Android Version an.

let's go

Sonntag, 18. Dezember 2011 von Farbfinsternis
vor gefühlten 100 Jahren habe ich ein Spiel namens "blox'n'balls" mit Blitz3D entwickelt. Es handelte sich dabei um einen Breakout Clone. Jetzt baue ich das Game mit Monkey neu und werde es für diverse Plattformen veröffentlichen.

Wer sich das Original Spiel anschauen möchte kann es sich gerne herunterladen (ca. 14MB), wer das ganze Paket, inklusive Entwicklerdaten, Rohdaten, Photoshop-Dateien etc. möchte kann sich das fette Paket http://www.colorflow.de/pub/download/bnb_full.zip herunterladen (ca. 117MB).

Für Vista und Windows7 User: Blitz3D hat ordentlich Macken. Öffnet bitte die Datei "blox.ini" mit dem Texteditor Eures geringsten Mißtrauens und fügt am Ende ein: "windowed" ... nur im Fenstermodus lässt sich bnb unter Vista oder Win7 problemlos spielen.

Das neue bnb wird vorrangig für Mobiles entwickelt und setzt somit auf Touchscreens. Während man auf Mouse-basierten Systemen die Maus nach links und rechts bewegt um das Paddle zu steuern, wischt man auf Mobiles das Paddle mit dem Finger nach links und rechts. Die Bwegung des Paddles im Moment der Kollision des Balls mit dem Paddle beeinflusst die neue Flugrichtung des Balls.

Alles was das originale bnb konnte wird natürlich auf die NG Version übertragen: Items welche manuell verwendet werden, Items welche automatisch beim einsammeln ausgelöst werden, der Shop um Items zu kaufen, die Online Highscore "iScore" ...

Wer schonmal einen Ball durch die Gegend schiessen will darf das unter http://www.sedm.de/monkey/bnb/html5/ tun.

user posted image