Posts filed under 'Projekte'
Habe das Timerproblem gestern noch irgendwie gelöst bekommen und kann mich nun den Szenen widmen. Hier möchte ich aber mal nicht einfach wild drauf los programmieren und widme der ganzen Sache lieber mal ein paar Bedenkminuten xD
Als erstes habe ich mich mal nach einem niedlichen Karteneditor umgesehen und tiled gefunden. Ist in Java geschrieben und läuft deshalb auch schön unter jedem ernst zu nehmenden Betriebssystem. Was ich auch noch sehr praktisch finde ist das die Karten in XML gespeichert werden und das man innerhalb des Karteneditors seinen Objekten Eigenschaften zuweisen kann.
Letzteres ist zum Beispiel dann praktisch, wenn man z.B. einen Notizzettel einfügt. Diesem könnte man also schon beim Entwerfen der Karte seinen Inhalt verpassen. Oder Teleportfeldern ihren Zielteleporter… Feine Sache :)
Intern werde ich die Szenen in ca. drei 2D Arrays speichern. In dem ersten kommen Bodenfliesen. Warum? Mir ist bei Robot 3 aufgefallen, das es nicht sehr schön aussieht, wenn man eine Mauer im Wald wegätzt, da sich dann dort eine weiße Fliese mitten im Wald befindet. Schöner wäre es, wenn dort meinetwegen brauner Waldboden angezeigt würde. Genau das ermöglicht mir dann mein erstes Array (nenne ich einfach mal Layer 0 xD).
Layer 1 wird alle weiteren nicht-NPC Spielobjekte enthalten. Und in Layer 2 kommen dann Dächer und alles andere unter dem Charlie lang laufen könnte. Befindet sich Charlie meinetwegen in einem Haus, wird einfach Layer 2 nicht mehr gezeichnet und wir haben den selben Effekt wie in Game of Robot mit den Dächern :)
NPCs und alle abgelegten Objekte kommen jeweils in eine Liste. Diese sind dann nicht sooooo lang das es zu Verzögerungen kommt und ich habe trotz allem die Möglichkeit mehrere Objekte auf ein und dem selben Feld zu haben. Denn diese Situation ergibt sich meines Erachtens nach nur, wenn der Spieler Objekte ablegt. Oder ist mir da was entgangen?
Momentan habe ich ja für jeden Objekttyp ein eigenes PNG, verträgt sich aber leider mit tiled nicht so toll. Werde hier also wieder auf ein einziges Bild für ein komplettes Tileset umstellen. Ein, zwei Änderungen an der Objektbasisklasse und auch dieses kleine Problem wäre gelöst.
Ich hoffe mal, das ich dies alles noch innerhalb dieser Woche schaffe, denn dann kann ich mich wieder an der GUI austoben ;)
Februar 8th, 2009
Weil in Löve alles so gut funktioniert hatte, habe ich dessen Timer nachgebaut und vorsorglich alle Float Variablen auf Integer umgestellt. Das brachte einen schönen Geschwindigkeitsschub und meine Animationen klappten auch perfekt. Aber eben wollte ich die flüssigen Objektbewegungen einbauen und alles fing an zu haken. Also alles wieder zurück auf Float umgebaut. Nun sind sie etwas flüssiger, aber er rechnet sich dumm und dämlich, 20 FPS Verlust :(
Es muss doch besser gehen… mal schaun…
Februar 7th, 2009
Die meisten Grundfunktionen sind ohne zu murren in ihren neuen Singleton-Heimen (Screen, Timer, Image) verschwunden. Die Game Klasse nimmt auch langsam Form an :)
Der nächste Schritt ist die Objekt Basisklasse für die Spielelemente. Erst einmal will ich sehen wie es sich in freier Wildbahn verhält, da es sich ja auch um eventuelle Animationen kümmern muss. Ohne Löve muss ich mich ja diesmal selbst darum kümmern *lach*
Was mir aber aufgefallen ist, in der Löve GORC Version hat jede Objektinstanz eine eigene Bildinstanz gehabt… was natürlich nicht unbedingt ressourcenschonend war. Diesmal habe ich aber darauf geachtet das ein Bild nur einmal geladen wird und bei jedem weiteren “Laden” aus dem Cache genommen wird.
Ich komme also voran… und das trotz Erkältung und extrem langsamem Denkprozess xD
Februar 6th, 2009
Vorhin war ich mir ja immer noch nicht sicher. Aber kaum hatte ich auf herc’s Kommentar geantwortet, zeigte sich D (wieder) ziemlich unbeeindruckt von meiner Vorstellung vom Vererben der Variablen und Funktionen einer Elternklasse an ihre Kiddies.
Ich gebe ja zu das ich nicht so recht weiss was ich mit Dingen wie “virtuell” und “protected” anfangen soll. Aber wenn ich z.B.:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
| class Image
{
...
void draw(uint x, uint y)
{
...
}
...
}
class Animation : Image
{
...
void draw()
{
draw(1, 1);
}
...
} |
habe, wieso um Gotteswillen der D Kompiler meckern muss, das die Animationsklasse keine draw(uint, uint) Funktion besitzt? Meinem Verständnis nach sollten sie diese doch aus der Image Klasse geerbt haben, oder? Ok, dank Google weiss ich das es so aus sehen muss:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
| class Image
{
...
void draw(uint x, uint y)
{
...
}
...
}
class Animation : Image
{
...
alias Image.draw draw;
void draw()
{
draw(1, 1);
}
...
} |
Puh! Langsam glaube ich wirklich, wer mit den OOP Prinzipien nicht im Schlaf umzugehen weiss, sollte von D die Finger lassen :(
Also… nehme ich C++, welches mit meinem Unwissen ein wenig gnädiger umgeht, und versuche es zu lernen ;)
Februar 5th, 2009
Hier auf meinem alten Laptop läuft GORC viel viel zu langsam. Das liegt zum größten Teil daran das Löve auf OpenGL setzt. SDL für eine 2D Spieleengine hätte vollkommen ausgereicht. Naja. Aber was nun?
Eigentlich ein ziemlich guter Zeitpunkt um GORC in einer “richtigen” Programmiersprache neu zu schreiben und gleich die SDL Bibliothek zu nutzen. Schließlich brauche ich hier nicht mehr als SDL, SDL_Mixer und SDL_Image. Später fürs Skripting noch Lua, aber bis dahin ist noch Zeit…
In die engere Wahl kommen dabei 4 Sprachen: C, C++, FreeBASIC und Digital Mars D. Letzteres wollte ich schon immer mal ein wenig genauer beschnuppern. Die Bibliotheken meiner Wahl unterstützen alle.
Zu C und C++ mag ich gar nicht allzu viel schreiben. Kann so ziemlich für alles kompiliert werden und ich liebe einfach deren Syntax :)
FreeBASIC ist quasi ein C mit QuickBasic Syntax. Kann aber leider nur für Windows und Linux kompilieren.
D soll wohl “besser” sein als C++. Inwiefern sich das auf ein einfaches Projekt wie GORC auswirkt, keine Ahnung. Das Kompilieren gestaltet sich dank DSSS viel einfacher als bei C/C++. Denn ich habe es noch nie wirklich hinbekommen, das meine Programmchen sich per ./configure && make kompilieren ließen. Ist mir einfach zu aufwendig das ganze *lach*
Hier auch mal ein kleiner Blick in Richtung D:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
| import derelict.sdl.sdl;
import tango.io.Console;
void main()
{
try {
DerelictSDL.load();
} catch (Exception e) {
Cout("Could not load the SDL shared library.\n");
return;
}
SDL_Init(SDL_INIT_VIDEO);
SDL_SetVideoMode(640, 348, 24, SDL_SWSURFACE);
SDL_WM_SetCaption("GORC", null);
mainLoop:
while (true) {
SDL_Event event;
while (SDL_PollEvent(&event)) {
switch (event.type) {
case SDL_QUIT:
break mainLoop;
default:
break;
}
}
SDL_Delay(10);
}
Cout("Oki Dokili.\n");
SDL_Quit();
DerelictSDL.unload();
} |
Also? Was nehm ich denn nun?
Februar 3rd, 2009
Was habe ich mich heute den ganzen Tag über tot gesucht *Kopf-schüttel*
Rahmen für Menüs und Dialoge wurden ohne Probleme gezeichnet, ok… machen wir noch schnell das Hauptmenü fertig und dann Feierabend. Nix da… Irgendwie passten entweder die Menüpunkte nicht in das Fenster, oder das Fenster war zu groß. Aber was kann man falsch machen, wenn ein Rahmenteil genau 8×10 Pixel groß ist und ein Buchstabe der Schriftart genauso groß ist? Simple Mathematik und doch passt irgendetwas nicht!
Ich musste tatsächlich erst ein paar Buchstaben der Schriftart mitteln Paint einen Rahmen verpassen um den Fehler zu sehen. Bis dahin hatte ich meinen Quellcode sicherlich schon an die tausend Mal ergebnislos korrektur gelesen. Die Standardfunktion von Löve für Bitmapschriftarten fügt (was ich natürlich nicht wusste) immer einen Pixel zwischen zwei Buchstaben ein. Aber zum Glück gab es auch noch eine Funktion, dank der ich diesen dummen Pixel abstellen konnte ;)
Nun gut, ich habe jetzt erst einmal genug vom Programmieren. Das Menü kann jetzt warten, da wird schließlich heute noch wer 18 und ist schon kräftig ohne mich am Feiern.
Herzlichen Glückwunsch, Brüderchen!
Januar 24th, 2009
Vorwort: Mir ist aufgefallen das ich meinen GORC Thread im Game of Robot Forum mittlerweile als Devblog mißbrauche. Dem werd ich jetzt mal dagegen wirken und meine Fortschritte und Gedanken hier im Blog posten… wo sie eigentlich auch hingehören ;)
So… habe die letzte Woche genutzt und alle Spielelemente in Klassen gestopft. Der Inhalt einer Szene steckt nun nicht mehr in einem 2D Array, sondern in zwei Lua Tabellen. Eine für die Roboter, später kommen eventuell auch alle anderen NPCs hier rein. Und in der anderen Tabelle stecken alle weiteren Elemente (Mauern, Gold, etc.).
Sinn und Zweck des ganzen ist es die Spiellogik überschaubarer zu gestalten. Anstelle langer if-then-else Blöcke steckt nun die gesamte Logik für ein bestimmtes Objekt in seiner Klasseninstanz. Mal ein Beispiel:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
| Gold = Class("Gold", Object)
GoldImage = love.graphics.newImage("images/gold-0.png")
GoldImage:setCenter(0, 0)
GoldSound = love.audio.newSound("sounds/gold.wav")
function Gold:init(x, y, params)
self.super:init(x, y)
self.ani = GoldImage
end
function Gold:canEnter(obj)
-- kann nur vom Spieler betreten werden
if not Player:made(obj) then return false end
return true
end
function Gold:onEntered(obj)
if not Player:made(obj) then return end
self:remove()
spieler.gold = spieler.gold + 1
love.audio.play(GoldSound)
end |
Ok. Ein sammelbares Objekt ist nun keine wirklich skripttechnische Herausforderung. Aber wie wäre es mit etwas komplizierteren?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
| Isolator = Class("Isolator", Moving)
IsolatorImage = love.graphics.newAnimation(love.graphics.newImage("images/isolator-0.png"), 16, 12, 0.1, 0)
IsolatorImage:setCenter(8, 6)
function Isolator:init(x, y, params)
self.super:init(x, y)
self.ani = IsolatorImage
self.speed = spieler.speed
end
function Isolator:canEnter(obj)
local dx, dy
-- kann nur vom Spieler betreten werden
if not Player:made(obj) then return false end
-- bewegt sich der Isolator bereits,
-- können wir ihn nicht betreten, bzw. in seine Richtung gehen
-- da wir sonst durch ihn hindurch laufen
if self:isMoving() then return false end
dx = self.x - obj.x
dy = self.y - obj.y
return self:canMoveTo(dx, dy)
end
function Isolator:canMoveTo(dx, dy)
local feld = Szenen:current():get(self.x + dx, self.y + dy)
if #feld == 0 then
self:setTarget(dx * 16, dy * 12)
return true
end
for i, e in pairs(feld) do
if not Isolator:made(e.obj) and not Barbwire:made(e.obj) then
return false
else
if e.obj:canMoveTo(dx, dy) then
self:setTarget(dx * 16, dy * 12)
return true
end
end
end
return false
end |
Ein anderer positiver Nebeneffekt ist die Möglichkeit nun mehrere Objekte auf dem selben Feld zu haben. Wenn man eine Szene das erste mal betritt, befindet sich im Normalfall nur jeweils ein Objekt auf einem Feld. Wer aber Game of Robot schon gespielt hat, weiss das irgendwann der Zeitpunkt gekommen ist, um sein Inventar auszumisten. Tada! Was liegt da näher als alles was im Moment nicht benötigt wird, einfach auf einen Haufen zu legen? Und wenn man es wieder benötigt, einfach auf das Feld laufen und man sammelt automagisch alles ein.
Leider Gottes kann ich mich nicht so wirklich über meine kleine “Errungenschaft” freuen, denn sie kostet Frames :( Das Intro mit seinen knapp 1000 Mauern ist von 60 FPS auf knapp unter 50 gefallen. In den Szenen hält es sich zwar noch in Grenzen (300 bis 400 Objekte) aber sobald man die Isolatoren herum schubst, kann man auch hier bis zu 10 Frames verschwinden sehen.
Habe zwar noch die ein oder andere Idee, wie ich den Verlust eingrenzen kann. Aber auf lange Sicht, sollte ich doch lieber nach einer etwas performanteren Lösung suchen. Welche aber auf keinen Fall das kompfortable Skripten der Objekte verkompliziert…
Jetzt möchte ich mir aber noch nicht den Kopf darüber zerbrechen, und überlege schon mich der GUI von GORC zu widment. Schliesslich wären das die Spielmenüs, die Messageboxen und vor allem das Inventar. Mal schaun, die Nacht hat ja gerade erst angefangen xD
Januar 22nd, 2009