Wie man den BCC gewinnt

Kommentare anzeigen Worklog abonnieren

Worklogs Wie man den BCC gewinnt

Wie man einen guten Blitz Code Contest veranstaltet!

Sonntag, 20. Mai 2012 von Xeres
Zum BCC gehört auch die rituelle Weitergabe der Veranstaltung an den Gewinner – was also tun, wenn man gewinnt?

Recherche & Vorbereitung
Kreative Momente kommen und gehen. Man kann nicht darauf vertrauen, dass man einen genialen Einfall bekommt, wenn man ihn braucht. Darum, wann immer euch etwas einfällt, schreibt es auf! Ein kleines, schwarzes Notizbuch mit Regelideen (auch wenn es nur Ausschnitte sind) ist seine Bytes in Gold wert.
Die meisten Wettbewerbe im Forum laufen schon eine ganze Weile – nutzt die vorhergegangenen Themen ruhig als Vergleich! Vielleicht war eure Idee so ähnlich schon mal dabei?
Wie präzise man auch glaubt, alle Möglichkeiten mit Regeln abgedeckt zu haben, es gibt immer einen Graubereich der gefunden (und ausgenutzt) wird.
Wenn ihr also nicht wisst, ob man etwas Bestimmtes mit einer Sprache umsetzen kann, solltet ihr euch an jemanden Wenden, der mehr Ahnung hat, bevor ihr die Regeln festlegt.

Faire Regeln
Als Veranstalter dürft ihr entscheiden, was erlaubt und verboten sein soll. Bestimmt ihr ein Codelimit, oder begrenzt ihr die Arbeit der Teilnehmer auf andere Art?
Lasst ihr alle Dateien zu, oder nur ausgewählte?
Das Codelimit ist der häufigste Streitpunkt: Ein Limit führt zu gecrunchtem (unleserlich verkürztem) Code und möglicherweise unsauberen, aber eben auch kreativen Lösungen. Ohne Limit ist der Code sauberer und die Teilnehmer können fröhlich vor sich hin coden, ohne extra auf die Begrenzung achten zu müssen.
Welche Variante ihr wählt, hängt von dem ab, was ihr erreichen wollt. Ich bin der Ansicht, interessante, kreative Beiträge entstehen durch etwas Zwang. Wenn ein Spiel in 20 KB Spaß macht, geht es auch mit 16 KB ohne Schnickschnack – das kondensierte, reine Spielprinzip sozusagen.
Grafik ist die zweite große Fragestellung. Gute Grafik täuscht ab und an über Probleme mit der grundlegenden Spielmechanik hinweg, aber wer verzichtet schon freiwillig auf etwas Hübsches?
Bilder aus dem Code heraus zu generieren ist eine Möglichkeit, die man für die Regeln im Hinterkopf behalten sollte.
Am besten sind immer einfache und eindeutige Regeln. 16 KB Grafik ist in Ordnung. „Benutzt wenige Bilder“ ist total ungeeignet.

Ein Thema
Vielleicht wollt ihr nicht zu komplizierte Regeln verwenden und stattdessen ein Ziel setzen, dass erreicht werden soll. Simple Themen (#29 - Uhr) können so verschieden gelöst werden, dass jeder etwas originelles Beitragen kann.
Ein spezielles Genre (#57 - Towerdefense) macht den Vergleich der Beiträge sehr viel einfacher – zieht aber vielleicht nicht alle gleichermaßen an.
Wichtig finde ich, dass es eine Richtung gibt; „Programmiert ein Spiel“ mit den Regeln „Alles ist erlaubt“ ist einfach uninteressant. Es ist keine Herausforderung dabei.

Die Zeit
Die Dauer des Wettbewerbs sollte man nicht zu kurz halten. Ideen finden, programmieren und testen braucht seine Zeit. Möglichst jeder sollte 3-4 Wochenenden an Gelegenheit erhalten – oder besser etwas mehr.
Zu viel Zeit kann aber auch nicht gut sein (insbesondere wenn keine Codegrenze vorgegeben ist). Zu viele Feature-Baustellen können ein Projekt zum Stillstand bringen (oder den ganzen Wettbewerb einstauben lassen) und es ist einfacher, in einem kurzen Zeitraum neu ein zu steigen als mit 3 Wochen Nachteil zu starten.
Also auch hier: Gut abwägen.

Gastgeberpflichten
Besonders zum Beginn des Wettbewerbs sollte man Anwesend sein um möglichst alle Fragen beantworten können (auch wenn die Antworten schon gegeben und mehrfach in den Regeln besprochen wurden).
Im Gegensatz zur Teilnahme, bei der wir wie zu Beginn des Worklogs andere Teilnehmer verschrecken wollten, geht es als Veranstalter darum, möglichst attraktive Konditionen zu bieten. Ein hübscher Header, ausgefeilte Regeln, ein ganz besonderes Thema, (digitale) Trophäen für die Gewinner und ihr werdet in der Erinnerung des Portals ewig leben!

Bis zum nächsten Contest, den ich versuche zu gewinnen!

Xeres.

Kompilieren für Linux

Mittwoch, 16. Mai 2012 von Xeres
Alles ist zusammengebaut. Was nicht rechtzeitig fertig wurde gestrichen. Jetzt bleibt nur noch die Release Version zu kompilieren und zeitig ab zu geben!
Für das beliebteste OS Windows ist das kaum ein Problem, aber was ist mit den lang bärtigen Hippie-Nerds, die die ganze Zeit von Freiheit schwafeln? Jede Stimme ist wichtig, also installieren wir uns ein Linux!
Natürlich nur virtuell, diese Betriebssysteme sind gefährlich für reale Hardware!

Schritt 1: Die virtuelle Umgebung
VirtualBox lässt euch multiple Rechner simulieren, um eure Software in allen möglichen Umgebungen zu testen. Keine Probleme mit Bootsektoren oder überschriebenen Festplatten!

Schritt 2: Ubuntu - weil darum!
Ein leerer, virtueller Rechner macht noch nicht viel Sinn, darum installieren wir ein Betriebssystem. Ubuntu ist nicht nur nett an zu sehen, der Paketmanager macht es auch einfacher, die nötigen Dinge zu installieren, um BlitzMax zur Arbeit zu bewegen.

Schritt 3: Installieren & Einrichten
Man installiere nun also die VirtualBox auf Windows und lege virtuell das Image der Ubuntu Distribution ein. Auch die virtuelle Installation braucht ein Weilchen, also macht euch zwischen drin einen Tee und spielt ein, zwei Runden eines genialen Spiels.
Sobald Ubuntu bereit ist, zieht eure Linux-Version von BlitzMax mittels eures Dropbox Accounts auf das System (Dropbox für Linux installieren, versteht sich). Noch werdet ihr die Module nicht bauen können, weil euch die nötigen Development Pakete fehlen.
Also auf zur Paketverwaltung!
folgende Pakete solltet ihr suchen, finden & installieren:
Code: [AUSKLAPPEN]
g++
libx11-xcb-dev
libxxf86vm-dev
libfreetype6-dev
libfltk1.1-dev
libxpm-dev
libglew1.6-dev

Jetzt sollten die Module & Beispielcodes kompilierbar sein!
Wenn nicht: Versucht heraus zu finden, welches Paket die fehlenden Header (.h) oder was auch immer enthält und installiert die dev Version. Viel Glück dabei!

Euer Projekt sollte nun nur noch minimal angepasst werden müssen, z.B. um den Pfad für einen Font korrekt zu setzen:
BlitzMax: [AUSKLAPPEN]
?Win32
SetImageFont(LoadImageFont(getenv_("windir") + "\fonts\" + "Arial.ttf", 24))
?Linux
SetImageFont(LoadImageFont("/usr/share/fonts/truetype/freefont/FreeSans.ttf", 24))
?


Und tadaa - ihr seid Multi-Platform und könnt euch auf 1-2 zusätzliche Stimmen freuen! Nicht vergessen das Projekt nochmal hübsch dar zu stellen bei der Abgabe. Wink

Aktuelle Version: SoupCraft.zip

In der nächsten Ausgabe:
Tröstende Worte für die Verlierer in eurer Siegesrede.
Wie man einen spannenden BCC veranstalltet.

Titel werden überbewertet

Sonntag, 13. Mai 2012 von Xeres
Der BCC dauert nicht mehr all zu lange. Machen wir's kurz:
Die Hintergrundebenen sind mit ein paar Bäumchen versehen und die Früchte haben ihre Plätze gefunden. Wenn man sie abschießt, wird ein Respawntimer gestartet. So bleibt der Spieler nie lange ohne Zutaten.
Jeder TEnemy-Type gibt über seine ToString-Methode seinen Namen zurück, wenn er getroffen wird und dieser wird aus dem aktiven Rezept gestrichen. Sobald das Rezept Komplett ist, wird die Reaktion-Funktion aufgerufen. Die nötigen Rezepte und Reaktionen sind nun recht einfach zu erstellen. Sie stehen zwar nur im Quellcode und nicht in einer fancy-pantsy txt Datei, aber das wäre das kleinste Problem.

user posted image

Was wirklich noch fehlt: Duh-du-duuuun SOUND.
Oft übersehen und übergangen werden regelmäßig Spiele erstellt, die nett an zu sehen sind aber keinen richtigen Charakter haben. Selbst eine kleine Prise Sound schafft die nötige Immersion um den Spieler von "Ich klicke Bilder weg" zu "Ich vernichte sinnlos Lebewesen" bringen. Visuelle Wahrnehmung ist nur ein Teil unseres Bildes von der Umgebung. Je mehr unterschiedliche Quellen zur Verfügung stehen, desto intensiver ist ein Erlebnis.
Welche Möglichkeiten haben wir, passende Sounds zu bekommen?

1) Clips aus einem Online Soundarchiv heraussuchen.
Auf den ersten Blick nicht schlecht: Schnell und einfach, oder? Kommt darauf an. Wenn man Stundenlang den perfekten Sound aus 1000 vorgefertigten Stücken heraussuchen muss, ist man sicher nicht besonders schnell. Zudem fehlt die Einheitlichkeit, wenn es keinen richtig passenden Sound gibt. Das Ergebnis kann dann tatsächlich schlimmer sein, als keinen Sound zu haben, weil Gehirne klasse darin sind Muster und kleine Fehler in diesen fest zu stellen.
Man greife auf vorgefertigte Sounds also nur zurück, wenn man es wirklich nicht selbst besser machen kann.

2) Sound Generatoren
Mal eben ein paar zufällige Sounds? Besonders wenn die Zeit drängt, wäre ein Tool toll, dass mit ein paar Parametern einen Sound generiert. Glücklicherweise gibt es exakt sowas: sfxr.
Der Ludum Dare Code-Contest läuft nur für 48 Stunden und genau für so arge Beschränkungen ist das Progrämmchen wie geschaffen. Ein Pling, Klich und Wuhuhuhu macht aus einem mittelmäßigen Beitrag einen Gewinner.

3) Sounds in Handarbeit
Wenn man ein halbwegs gutes Mikrofon sein eigen nennt, kann man auch versuchen, Soundeffekte per Hand zu erstellen. Bleche Schütteln um Donner zu erzeugen, Tüten knistern für elektrische Entladungen oder sanft ins Mikrofon hauchen um statisches rauschen zu bekommen.
Nachtteile sind Offensichtlich: Zeit und etwas können sind Voraussetzung. Dafür klingt es nicht zu künstlich und es ist selbst gemacht.

4) Bezahlt einen richtigen Künstler
Es geht nichts über Handgeschmiedeten, eigens angepassten Sound. Am besten mit epischer Musik dazu.
Das gilt auch für Grafik. Und Code, wenn wir ehrlich sind.
Ein kreativer Schreiberling für die Handlung wird auch nicht schaden.
Am besten also, ihr leitet nur das Team.
Wer soll das alles Bezahlen? Kickstarter, ihr Noobs!

Wir sehen uns dann!

Aktuelle Version: SoupCraft.zip

In der nächsten Ausgabe:
Was heißt "Abgabe war gestern"?!

Steht ein Pilz im Wald, kommt ein Jäger und trinkt's au

Montag, 30. April 2012 von Xeres
Mit Kirschkernen ist nicht zu Spaßen! Die zerpflücken einen Pilz blitzschnell in mikroskopisch kleine Teilchen (vor allem, weil Partikeleffekte noch etwas warten können).

user posted image

Die möglichen Ziele sind Abkömlinge eines TEnemy-Objektes (was Namen angeht, bin ich nicht zu kreativ...) und können sich in drei Entfernungsstufen befinden.
Falls sich ein Objekt hinter einem Hügel versteckt, kann man es nicht treffen. Wir wir wissen, unterliegen Fermionen dem Pauli-Prinzip.
Jetzt muss das Gemüse nur noch an ein paar Stellen gepflanzt werden (zufällige Verteilung sähe einfach doof aus). Punkte gibt es auch schon, die mit dem selben Skalierungsfaktor bedacht werden: Was weiter weg ist, z.B. 50% kleiner ist, gibt doppelt so viele Punkte. Ist ein simples Konzept, darum lasst es mich für euch (nicht du persönlich, du bist klüger als der Rest hier *zwinker*) nochmal erklären: Die Schwierigkeit des Zielens wird durch eine höhere Punktzahl kompensiert - damit sollte der Spieler alle Ebenen beachten.

Nun brauchen wir nur noch ein paar Ziele, die möglichst Nachwachsen (timer) und dann in einen Kessel geworfen werden (rezepte).

Wie ihr an entsprechender Stelle nachschlagen könnt, hat sich kaum jemand getraut, seine Projektideen vor zu stellen! Ich würde ja mit dem Finger auf sie deuten und sie auslachen, aber dazu habe ich zu viel Klasse und ich lasse mich auch ungern beim popeln unterbrechen (nichts geht über die direkte Massage des Gehirns).
Wie dem auch sei, taktischer Erstschlag gelungen.

Aktuelle Version: SoupCraft.zip

In der nächsten Ausgabe:
Wie man Suppe kocht.
Realitätsgetreue Soundkulissen.
Wie man aus Blei Gold macht.

...und es macht jetzt schon mehr Spaß als WoW!

Dienstag, 24. April 2012 von Xeres
Wo waren wir... achja, unser Highscore sortiert sich, aber der User sieht davon noch nicht all zu viel.
Die Draw-Funktion ist schnell angepasst - aber DrawText hat das kleine Problem, dass der Text immer Linksbündig angezeigt wird. Für Zahlen nicht optimal, überschreiben wir diese Funktion also einfach mit einer besseren.

BlitzMax: [AUSKLAPPEN]
Function DrawText(_txt:String, _x:Float, _y:Float, _HX:Float = 0.5, _HY:Float = 0.5)
Local scale_X:Float, scale_Y:Float
GetScale(scale_X, scale_Y)
brl.max2d.DrawText(_txt, Int(_x - (Float(TextWidth(_txt)) * scale_X * _HX)), Int(_y - (Float(TextHeight(_txt)) * scale_Y * _HY)))
End Function


Mit Beachtung der Skalierung und des Handle-Faktors - wie überall normalerweise mittig zentriert - steht einem richtigem Highscore nichts im Wege.
Leider ist die Liste beim Start leer, laden wir also eine! ...wir haben noch keine gespeichert? Falls es soweit kommt, füllen wir den Highscore einfach mit ein paar Werten.

BlitzMax: [AUSKLAPPEN]
Function Load()
Local File:TStream = ReadStream(THighscore.FilePath)
If Not(File) Then THighscore.Fill() ; Return

While Not(File.Eof())
Local n:String = File.ReadLine()
Local p:Int = File.ReadInt()
THighscore.Add(n, p)
Wend

File.Close()
End Function

Function Fill()
For Local i:Int = 0 Until 10
THighscore.Add("Xeres", i * i * 15 + i * 10 + 10)
Next
End Function


Die Punkte lassen sich später noch anpassen, erst mal brauchen wir nur Platzhalter.
Okay! Jetzt gehen wir aber sicher, dass wir alles nötige speichern! Freundlicherweise stellt BlitzMax die OnEnd-Funktion bereit, der man Funktionen nennen kann, die vor dem Beenden noch ausgeführt werden sollen.

BlitzMax: [AUSKLAPPEN]
Global Points:Int, Time:Int, PlayerName:String = getenv_("USERNAME")

LoadOptions()
THighscore.Load()

OnEnd(THighscore.Save)
OnEnd(SaveOptions)


Das funktioniert garantiert, solange das Spiel nicht abstürzt und wir müssen uns keinen Kopf machen, ob wir jede Form von abbrechen/beenden bedacht haben.
Man beachte: Keine Klammern - die Funktionen werden nicht aufgerufen!
Man beachte außerdem: Erst Variablen mit Standardwerten füllen, dann ggf. laden.
In den Optionen steht momentan nur der Spielername, aber solange wir den Spieler nicht freundlich am Arm berühren können, gibt nichts so viel Sympathie, wie der Simple Fakt, dass wir uns seinen Namen gemerkt haben.
Wenn er mehr als einmal Spielt, selbstredend.

Aktuelle Version: SoupCraft.zip

In der nächsten Ausgabe:
Zielen & Pusten - wie man Kirschkerne auf tödliche Geschwindigkeiten bringt.
Wie man ein Hausboot aus Zahnstochern baut.
Zombies selbst gemacht: Gräber entweihen für Anfänger.

Dein wahrer Name lautet... %USERNAME%

Montag, 23. April 2012 von Xeres
Richtige Spiele kann man verlieren. Entweder man muss von vorne anfangen oder man bekommt einen gehörigen Malus (das Gegenteil von Bonus). Um zu einem Spielbaren Zustand zu kommen, sollte man sich also so früh wie möglich daran machen, den Spieler verlieren lassen zu können.
In unserem Fall ist das das Zeitlimit.
Und da wir gerade dabei sind, kümmern wir uns doch noch um Punkte und den Namen des Spielers. Um später gar keine Probleme zu bekommen, sollten diese Variablen vor dem Start des Spiels auf ihre Startwerte zurückgesetzt werden.

BlitzMax: [AUSKLAPPEN]
Global Points:Int, Time:Int, PlayerName:String = getenv_("USERNAME")

'[...]

Function ResetGame()
Points = 0
Time = ms + 90500 '* 1:30'50
End Function


Gut, gut. Um die die Zeit gescheit dar zu stellen, formatieren wir sie in ein netteres Format.

BlitzMax: [AUSKLAPPEN]
Function MakeTime:String(_time:Int)
'* _time [ms]
Local parts:String[]

'* Minuten
If _time >= 60000 Then
parts:+[String(_time / 60000)]
_time = (_time Mod 60000)
Else
parts:+["0"]
EndIf

'* Sekunden
If _time >= 1000 Then
parts:+[String(_time / 1000)]
If _time / 1000 < 10 Then parts[parts.Length - 1] = "0" + parts[parts.Length - 1]
_time = (_time Mod 1000)
Else
parts:+["00"]
EndIf

Return ":".join(parts)

End Function


Nun kann das Spiel verloren gehen!

BlitzMax: [AUSKLAPPEN]
Function Run_Play()

'* Spiel vorbei!
If Time - ms <= 0 Then
FlushKeys()
GameState = GS_HighIn
Return
EndIf

DrawText(MakeTime(Time - ms), gfx_w *.02, gfx_h *.05)
DrawText(Points, gfx_w *.02, gfx_h *.1)

If mh2 Then Time:+5000
If mh1 Then Time:-5000

End Function


Den Spielernamen, den wir geschickter Weise vom Betriebssystem erfragt haben, ist ein guter Anfang, aber vielleicht will sich jemand ja anders nennen (wir tun mal einfach so, als hätte der Spieler nicht-imaginäre Freunde), also braucht es eine Input-routine.

BlitzMax: [AUSKLAPPEN]
Function Run_HighIn()

DrawText(PlayerName + "<", gfx_w *.5, gfx_h *.5)
Local c:Int = GetChar()
If c > 31 And PlayerName.Length < 16 Then PlayerName:+Chr(c)

If KeyHit(KEY_BACKSPACE) And PlayerName.Length > 0 Then PlayerName = PlayerName[..PlayerName.Length - 1]
If KeyHit(KEY_ENTER) Then
THighScore.Add(PlayerName, Points)
GameState = GS_High
EndIf

End Function


Das bringt uns zum nächsten Problem: Die Highscoreliste.
Zu kompliziert ist es nicht. Wir wollen:

  • Namen & Punkte verwalten
  • Nach Punkten Sortieren
  • Das ganze Anzeigen
  • und natürlich Speichern/Laden

Wir müssen uns nicht großartig mit Sortieralgorithmen aufhalten - wir vertrauen einfach darauf, dass BlitzMax eine Liste mit ~11 Einträgen in der durchschnittlichen Lebensspanne eines Menschen verarbeiten kann.
Damit das funktioniert, überschreiben wir nur die Compare-Methode, die jedes Objekt besitzt. Selbstredend benutzen wir die Punktedifferenz von zwei Objekten, um einen Vergleich an zu stellen.
Gleiche Punktzahlen bedeuten, das kein Tausch stattfindet - warum ist das signifikant? Ganz einfach: Stellt euch vor, ihr habt drei Tage versucht den Highscore eures (cheatenden) Freundes zu knacken, aber ihr habt trotzdem nur exakt seine Punktzahl erreicht. Es gibt keine zwei Personen auf einem Platz, also muss einer von euch oben stehen.
A) Der Eintrag wurde nicht übertroffen, also steht ihr unter eurem Freund.
B) Der Eintrag ist neuer, also steht ihr über eurem Freund.
Hier gibt es keine wirkliches richtig oder falsch - es ist eine Designentscheidung, die ihr bewusst treffen solltet. Ich für meinen Teil halte es für positiver, wenn der neue Eintrag weiter oben steht.
Aus diesem Grund werden neue Einträge mit AddFirst in die Liste aufgenommen.

Und um sicher zu gehen, dass alles läuft, wie es soll noch ein kurzer Test:
BlitzMax: [AUSKLAPPEN]
Type THighscore

Field Name:String, Points:Int

Global List:TList = New TList

Function Add(_name:String, _points:Int)
Local H:THighscore = New THighscore
H.Name = _name
H.Points = _points
THighscore.List.AddFirst(H) '* Neue Einträge stehen weiter oben in der Liste
THighscore.List.Sort() '* Sort() benutzt die Compare-Methode des Objekts, siehe unten
End Function

Method Compare:Int(withObject:Object)
Local H2:THighscore = THighscore(withObject)
Return H2.Points - Self.Points
End Method

Method ToString:String()
Return Self.Name + " ~~ " + Self.Points
End Method

Function Draw()
'* Nur zu Debugzwecken
For Local H:THighscore = EachIn THighscore.List
Print(H.ToString())
Next
End Function

End Type

'* Testen!
THighscore.Add("Ich", 5)
THighscore.Add("Du", 10)
THighscore.Add("Ich2", 2)
THighscore.Add("Du2", 10)
THighscore.Draw()


Aktuelle Version: SoupCraft.zip

In der nächsten Ausgabe:
Wie man Daten geschickt speichert und lädt.
Warum man stirbt, wenn man sich ausschließlich von Hasenfleisch ernährt.
Wie man sein Zimmer mit einer kleinen Quantität TNT innerhalb von 5/100 Sekunden perfekt aufräumt.

Das Menü und wie man es präsentiert

Sonntag, 22. April 2012 von Xeres
Das Beispielhafte Bild für Knöpfe vom letzten mal habe ich noch mit einem netten Glanzlicht versehen, damit es hübsch Web2.0-glossy aussieht und die restlichen Knöpfe gemacht (siehe Screenshot & Download).

Ein sauberer, aufgeräumter Eindruck ist wichtig, darum vergesst nie, mindestens einen Unterordner zu erstellen, der alle Medien enthält.
Nachdem nun Buttons funktionieren, wird es Zeit, den Code etwas zu strukturieren. Die Hauptschleife enthält nur noch das nötigste. Welcher Programmteil ausgewählt wird, ist in der Variable GameState gespeichert und die möglichen Zustände in Konstanten - leicht lesbar und 67% weniger Fehlerbehaftet als überall Zahlen ein zu tragen.

BlitzMax: [AUSKLAPPEN]
Const GS_MainMenu:Int = 0, GS_Play:Int = 1, GS_High:Int = 2, GS_Achi:Int = 3
Global GameState:Int = GS_MainMenu

'[...]

Select GameState
Case GS_MainMenu; Run_MainMenu()
Case GS_Play; Run_Play()
Case GS_High; Run_High()
Case GS_Achi; Run_Achi()
End Select

'[...]

Function Run_MainMenu()

'* Spielen
If ButtonImg(MenuButtons, 0, gfx_w *.5, gfx_h *.2) Then
GameState = GS_Play
EndIf

'* Highscore
If ButtonImg(MenuButtons, 2, gfx_w *.5, gfx_h *.4) Then
GameState = GS_High
EndIf

'* Errungenschaften
If ButtonImg(MenuButtons, 4, gfx_w *.5, gfx_h *.6) Then
GameState = GS_Achi
EndIf

'* Beenden
If ButtonImg(MenuButtons, 6, gfx_w *.5, gfx_h *.8) Then
End
EndIf

End Function


Das Menü läuft damit. Ein guter Zeitpunkt, den psychologischen Kampf gegen den Rest der Teilnehmer mit einem taktischen Erstschlag zu eröffnen. Demotiviere Teilnehmer sind verlierende Teilnehmer!
Wichtige Informationen sind natürlich:

  • Der Titel
    Er sollte sich gleich zu Anfang in das Gedächtnis des Wahlvolkes einbrennen.
  • Das Genre
    Es sollte ansprechend formuliert werden, was den Spieler erwartet.
  • Die (geplanten) Features
    Versprecht nichts, was ihr nie einhalten könnt aber übertreibt ruhig zu 25% - ihr wisst ja, wie Werbung funktioniert.
    Offensichtlich falsche Sätze, die man nie benutzen sollte sind sowas wie "Stürzt nicht ab" oder "Macht Spaß" - lasst das A die Wähler entscheiden weil es B total subjektiv ist.
  • Ein Screenshot
    Benutzt immer einen guten, vorzeigbaren Screenshot. Nehmt nicht den erst besten, sondern einen, der tatsächlich etwas interessantes vorzeigt. Keine weiten, leeren Flächen; bunte, effektreiche Explosionen!
    Und bindet das Bild in einer Foren freundlichen Größe direkt ein. Lasst nie jemanden eure Werbung suchen!
  • Der Download
    Kein muss (zu diesem Zeitpunkt) aber es zeigt ganz deutlich "Ja, ich arbeite daran und meine maßlosen Übertreibungen der Features werden tatsächlich bald Wirklichkeit!".
    Auch hier: Ein hübscher, aussagekräftiger Direktlink ist einem Sonderzeichendurchfall von einer Webadresse, die zu einer superlahmen, von Werbung strotzenden Hostingseite führt, weit überlegen!


Beispiel

Aktuelle Version: SoupCraft.zip

In der nächsten Ausgabe:
Wie erzeugt man waffenfähige Kirschkerne?
Highscores sammeln, sortieren und Speichern.
Warum Feuchtigkeitscreme kein Ersatz für Ketchup ist.

Buttons mit Inkscape

Freitag, 20. April 2012 von Xeres
user posted image

Für ein Menü brauchen wir Knöpfe. Die passenden Grafiken erstellen wir mit Inkscape - wir wollen schließlich gewinnen! ...und Vektorgrafiken lassen sich einfach vergrößern und verkleinern ohne an Qualität zu verlieren. Inkscape ist kostenlos und leicht zu bedienen.

Schritt 1: Ein Rechteck muss her
Buttons sind Quaderförmig, also ziehen wir mit dem Rechteck-Tool eine passende Fläche auf. Die aggressiven, spitzen Ecken bändigen wir mit einem kleinen Zug an den runden Anfassern und machen sie damit zu sanften, abgerundeten, verständnisvollen Ecken.

user posted image

Danach wechseln wir zum Zeiger-Tool und setzen Höhe & Breite des Buttons auf die Werte, die wir brauchen.

user posted image

Schritt 2: Die richtige Schrift
Es gibt sooo viele Schriftarten - welche passt hier am besten?

user posted image

Gratulation! Du hast die richtige Antwort C gewählt!

A: Comic Sans. Wenn es Webseiten gibt, die sich der Ausrottung einer Schriftart verschrieben haben, sollte man sich sehr sicher sein, dass man sie einigermaßen richtig einsetzt. Hier ist sie sicherlich fehl am Platz.

B: Chiller. Was exakt ist so gruselig in dem Spiel? Wir machen leichte Unterhaltung, aber nichts, was einem einen Schauer über den Rücken jagd.

C: Starcraft. Oh ja! Wenn unser Spiel ähnlich heißt und ähnlich aussieht... dann muss es doch auch total gut sein, oder?

D: Walbot. Hey, wir wollen nicht an einen altdeutschen Gasthof erinnern. Außerdem ist es saumäßig zu lesen!

Was war das? Ihr habt keine 1000 Schriften zur Auswahl? Na dann aber los ins Internet! Es gibt viele Seiten, auf denen Schriftarten kostenlos und frei benutzbar auf euch warten. Starcraft fand ich zum Beispiel auf DaFont.

Schritt 3: Der Farbton muss stimmen
Knopf und Beschriftung sind schön und gut, aber ohne die richtige Farbe wird das nichts. Die Schrift sollte lesbar sein, also achten wir auf genügend Kontrast.

user posted image

Der Hintergrund bekommt einen Kreisförmigen Farbverlauf, der in die Länge gezogen die Hintergrundbeleutung spielt. Ein dunkles Rot im Vordergrund sorgt für Kontrast.
Wir duplizieren das ganze ([Strg]+D) erst einmal. Dann drehen wir im Hintergrund die Farbintensität etwas höher. Die Schrift wird ein sattes Gelb. Als finish dublizieren wir die Schrift nochmal und machen sie unscharf.
Der Rest ist einfach: Alle Objekte eines Knopfes werden mit [Strg]+G zu einer Gruppe zusammengefasst. Wir richten die Buttons aneinander aus, gruppieren sie nochmals zu einem Objekt zusammen und exportieren das Ergebnis:

user posted image

Codetechnisch brauchen wir nun eine Funktion, die einen von beiden Frames anzeigt (abhängig von der Maus) und einen Mausklick registriert.

BlitzMax: [AUSKLAPPEN]
Function ButtonImg:Int(_img:TImage, _frame:Int, _x:Int, _y:Int)
If mouseInRect(_x - _img.width / 2, _y - _img.height / 2, _img.width, _img.height)
DrawImage(_img, _x, _y, _frame + 1) '* Hover Frame
If mh1 Then Return True '* Mausklick berichten
Else
DrawImage(_img, _x, _y, _frame) '* Inaktiver Frame
EndIf
End Function

Function mouseInRect:Int(x:Int, y:Int, w:Int, h:Int)
If mx <= x Then Return False
If my <= y Then Return False
If mx >= x + w Then Return False
If my >= y + h Then Return False
Return True
End Function


Die mouseInRect Funktion ist bekannt aus Klassikern wie Mouse on the Road.
Jetzt noch alles zusammenbauen...

BlitzMax: [AUSKLAPPEN]
SuperStrict

AppTitle = "SoupCraft"
Global gfx_w:Int = 1024
Global gfx_h:Int = 512
Graphics(gfx_w, gfx_h, 0, 60)
AutoMidHandle(True)
SeedRnd MilliSecs()
Local Frametimer:TTimer = TTimer.Create(60)

Global mx:Int, my:Int, mh1:Int, mh2:Int, md1:Int, md2:Int, ms:Float

Global test:TImage = LoadAnimImage("ButtonStart.png", 256, 64, 0, 2)

SetColor(255, 255, 255)
SetClsColor(0, 0, 0)
SetBlend(ALPHABLEND)

Repeat
Cls

ms = MilliSecs()
mx = MouseX()
my = MouseY()
mh1 = MouseHit(1)
mh2 = MouseHit(2)
md1 = MouseDown(1)
md2 = MouseDown(2)

If ButtonImg(test, 0, gfx_w *.5, gfx_h *.5) Then
End
EndIf


Flip(0)
Frametimer.Wait()
If KeyHit(KEY_ESCAPE) Or AppTerminate() Then End
Forever
End

Function ButtonImg:Int(_img:TImage, _frame:Int, _x:Int, _y:Int)
If mouseInRect(_x - _img.width / 2, _y - _img.height / 2, _img.width, _img.height)
DrawImage(_img, _x, _y, _frame + 1) '* Hover Frame
If mh1 Then Return True '* Mausklick berichten
Else
DrawImage(_img, _x, _y, _frame) '* Inaktiver Frame
EndIf
End Function

Function mouseInRect:Int(x:Int, y:Int, w:Int, h:Int)
If mx <= x Then Return False
If my <= y Then Return False
If mx >= x + w Then Return False
If my >= y + h Then Return False
Return True
End Function


In der nächsten Ausgabe:
Wie man die Spielzustände organisiert.
Wie man dreißig Tage in einer Salzwüse überleben kann.
Wie man einen Grizzlybär umschubst.

Planungsphase

Donnerstag, 19. April 2012 von Xeres
Die Super-duper Spielidee ist gefunden, nun braucht es etwas Details!
Ich mag es, das Spielfeld erst einmal mit einer kleinen Skizze zu definieren.

user posted image

Wichtige Informationen müssen später einen gut sichtbaren Platz finden (Zeit, Munition).
Um eine Tiefenwirkung zu erzielen, benutzen wir parallax scrolling (Bodenwellen, Hügel, Berge) und ein paar Hindernisse wie Bäume, Gebäude usw. machen das Spiel nicht zu einfach.
Schießen tut man nicht nur auf Hühner, sondern auch auf Obst & Gemüse (mit Kirschkernkannonen und Granatapfelwerfern) - und kann daraus Suppen kochen!
Jedes Rezept kann eine Wirkung bekommen, z.B. mehr Zeit, mehr Munition, bessere Waffen - der übliche Upgrade Spaß!
Das befriedigt Jäger & Sammler Instinkte - zusammen mit einem Highscore (besser als andere sein zu wollen hat sich evolutionstechnisch auch durchgesetzt) sollte das süchtig machen.

Dann mal ran an den Code!
Selbstverständlich benutzen wir BlitzMax. Wir wollen schließlich gewinnen.

Erst mal nur der Standardmäßige Anfang.
BlitzMax: [AUSKLAPPEN]
SuperStrict

AppTitle = "SoupCraft"
Global gfx_w:Int = 1024
Global gfx_h:Int = 512
Graphics(gfx_w, gfx_h, 0, 60)
AutoMidHandle(True)
SeedRnd MilliSecs()
Local Frametimer:TTimer = TTimer.Create(60)

Global mx:Int, my:Int, mh1:Int, mh2:Int, md1:Int, md2:Int, ms:Float

SetColor(0, 0, 0)
SetClsColor(255, 255, 255)
SetBlend(ALPHABLEND)

Repeat
Cls

ms = MilliSecs()
mx = MouseX()
my = MouseY()
mh1 = MouseHit(1)
mh2 = MouseHit(2)
md1 = MouseDown(1)
md2 = MouseDown(2)


Flip(0)
Frametimer.Wait()
If KeyHit(KEY_ESCAPE) Or AppTerminate() Then End
Forever
End


Ein Breitbildformat ist Trumpf: genug Übersicht in der Richtung, die wichtig ist. Was am Himmel oder am Boden passiert, kümmert den Spieler nicht.

Kommentar-Kommentar
Zitat:
Kein 3D? Ich hab beim letzten BCC alles falsch gemacht
In der Tat!
Zusätzliche Dimensionen machen nichts besser - nur anders. Schlimmstenfalls kauert der Spieler orientierungslos, verwirrt & verängstigt in einer Ecke, bestenfalls denkt er sich "Mit einem HDR shader hätte ich vielleicht sogar Spaß...".

In der nächsten Ausgabe:
Wie man ein Menü zusammen zaubert.
Wie man die dafür passende Grafik erstellt.

Idee & Konzept

Mittwoch, 18. April 2012 von Xeres
Das Wichtigste bei einem Wettbewerb ist es, sich mit den Regeln vertraut zu machen! Gute Stichpunkte, die es ein zu halten gilt, finden sich meistens im Eingangstext:

Zitat:
Programmiert ein Spiel, in dem eine oder mehrere Früchte vorkommen. [...]
- Keine Codebegrenzung
- Maximale Größe aller externen Ressourcen: 2 MiB
[...] ihr könnt einsenden bis einschließlich zum 16. Mai 2012


Okay, nun muss man sich Fragen:
Was für ein Spiel möchte ich schreiben?
Was für ein Spiel kann ich schreiben?
Was für ein Spiel wird beim Volk gut ankommen?

Ein WoW 2.0 kommt sicher gut an, aber weder von Zeit noch Umfang passt es in die Regeln.
Ein billiger Snake-Clone wäre eine sichere Wahl. Jeder weiß schon, was er spielt - aber das ist mir wirklich zu langweilig.
Lieber etwas brutales - ein Ballerspiel! Kein 3D, nicht zu kompliziert - wer erinnert sich an dieses kleine Werbespiel für Johnnie Walker?

Jaaa, das ist gut.
Um was spielt man? Punkte!
Wie lange? Bis die Zeit abläuft!
Auf was schießt man...?
user posted image

Welchen Titel nimmt man, um maximalen Erfolg zu garantieren?

SoupCraft

In der nächsten Ausgabe:
Wie man ein Projekt richtig beginnt.
Wie man mit einschüchternden Screenshots und/oder Menüdemos die Konkurrenz klein hält.
Und wo sind da die Früchte?

Bis dann!

Links:
BCC #61 - Früchte