• Anmelden

1

Montag, 27. August 2007, 16:54

Dateien Speichern

Hi, ich hab mal folgende Frage:
wie kann ich eine Datei erstellen, in die ich alles rein speichern kann, was ich will.

wobei die Frage eigentlich Lauten sollte:
mit welcher Methode kann ich alle möglichen Sachen in eine Datei speichern?

also auch Sachen, wie Bitmaps, die mit Marshall nicht gespeichert werden können.

xelax90

2

Montag, 27. August 2007, 18:24

Hallo,
dawäre erstmal die Frage welche Sachen kannst du denn nicht mit Marshell speichern? Und für Bitmaps gibt es Script, dass man sie als png speichern kann, da es eine jap. Seite war, finde ich die net mehr, aber auf rmxp.org gabs so ein ähnliches -> http://www.rmxp.org/forums/showthread.php?t=19607

Und sollte man nicht von vornerein wissen, was man speichern will?

Gruß Sven
:information: YAams
:rainbow: Random Signatur
Bild





Weitere Informationen | Download
Mit Support für RGSS Project 1!
Bild

3

Montag, 27. August 2007, 20:04

Marshaling ist nur das Umwandeln eines Objektes in String-Form und das Anschließend wieder einlesen. Dafür gibt es zwei Methoden:
marshal_load und marshal_dump
Ein Objekt das diese zwei Methoden definiert hat, kann problemlos eingelesen werden. Manche Objekte wie Bitmaps, Procs und Methoden können standardmäßig nicht eingelesen werden. Für sie musst du die zwei Methoden also erst definieren.

Quellcode

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Meine_Klasse
  attr_accessor(:a, :b, :c)
  def marshal_dump
    "#{@a}, #{b}, Welt"
  end
  def marshal_load(string)
    array = string.split(",")
    initialize(*array)
  end
  def initialize(a,b,c)
    @a = a
    @b = b
    @c = c
    p @a, @b, @c
  end
end
mein_objekt = Meine_Klasse.new("eine", "schöne", "stadt") #=> eine schöne Stadt
File.open("meine_datei.dat", "w+") {|output| Marshal.dump(mein_objekt, output)}
#Objekt ist nun abgespeichert. Jetzt soll es wieder geladen werden
geladenes_objekt = File.open("meine_datei.dat", "r+") {|input|Marshal.load(input)} #=>eine schöne Welt


Das Beispiel zeigt wie man seine eigenen marshal_load und marshal_dump Methoden für ein Objekt definieren kann. Dasselbe wäre auch bei Bitmaps möglich, allerdings würde ich mich dann doch erst einmal intensiv mit dem Aufbau von PNGs, dem Komprimieren von Bilddaten etc. beschäftigen. Wenn du sonst jede Farbe einfach in Textform abspeicherst und wieder einlädst hast du eine Riesendatei die wahrscheinlich noch wesentlich mehr Speicherplatz verbraucht als eine bmp.

Ansonsten gilt neben Marshaling:

Quellcode

1
2
3
File.open(dateiname, modus) do |input_output_stream|
  #mache etwas mit dem input_output_stream
end


Als modi gibt es "r+" um eine Datei von Anfang an zu lesen, "w+" um eine Datei neu zu erstellen und zu beschreiben, "a+" um eine Datei am Ende zu öffnen und so neue Einträge reinzuschreiben. Zudem kannst du noch "rb+", "wb+" und "ab+" schreiben um die Datei als binärstrom zu lesen/schreiben. Um eine Datei zu lesen stehen dir die Methoden
input.read #=> liest standardmäßig die ganze Datei in einen String
input.readlines #=> liest jede Zeile in ein Arrayelement
Zum schreiben:
output.write(obj) #=> Schreibt String-obj in Datei

Genaueres findest du auch in der Helpdatei des Makers.
Bild
RMXP Grundkurs
1 2 3
Ruby/RGSS-Kurs

4

Dienstag, 28. August 2007, 21:06

erstmal thx euch beiden.

@Abt:
in einer Bild datei will ich das nicht speichern, weil es 1. so ca 4 oder 5 bitmaps sind und die aber gar nicht vom Spieler gesehen werden sollen außerhalb vom Spiel


@Kai:
wie man Marshal benutzt, weiß ich auch^^
das mit den Marshal Methoden allerdings noch nicht^^

aber ich hab auch schon an sowas gedacht.
das Problem bei der ganzen Sache ist wirklich das Kodieren eines Bitmaps in einen String.

weißt du, wie und wo man da vlt. Informationen her bekommen könnte?

5

Dienstag, 28. August 2007, 22:45

erstmal thx euch beiden.@Abt:
in einer Bild datei will ich das nicht speichern, weil es 1. so ca 4 oder 5 bitmaps sind und die aber gar nicht vom Spieler gesehen werden sollen außerhalb vom Spiel

Wenn es nur darum geht, dann verschlüssel doch die Grafiken einfach. Sprich: Du speicherst sie als PNG ab, verschlüsselst sie und entschlüsselst sie beim Starten des Spiels wieder.

Als Verschlüsselungsmechanismus kannst du praktisch alles verwenden.

Quellcode

1
2
3
4
5
6
7
8
9
10
11
12
13
14
GEHEIMZAHL = 5
datei = ""
File.open("meine_grafik.png", "rb+") do |input| datei = input.read end
#verschlüssel die Datei!
crypted_data = ""
datei.each_byte do |byte|crypted_data <<  (byte^GEHEIMZAHL) end
#Schreibe verschlüsselte Datei!
File.open("meine_grafik.png", "wb+") do |output| output.write(crypted_data) end
 
#Lese verschlüsselte Datei
File.open("meine_grafik.png", "rb+") do |input| datei = input.read end
#Entschlüssele die Datei!
encrypted_data = ""
datei.each_byte do |byte| encrypted_data << (byte^GEHEIMZAHL) end


Das Problem an der ganzen Sache: Wie willst du aus einem String ein Bitmap erzeugen? Denkbar wäre höchstens, du speicherst es kurz als Datei ab und lädst es dann als Bitmap. Sonderlich sicher ist das natürlich nicht, andererseits wirst du dein Spiel wohl auch kaum vor Computerprofis schützen wollen, sondern vor dem einfachen Spieler.
Bild
RMXP Grundkurs
1 2 3
Ruby/RGSS-Kurs

6

Dienstag, 28. August 2007, 23:22

worum es mir geht ist eigentlich einfach nur das Speichern eines Savegames.

ich hab so ziemlich alle skripts im maker umgeschrieben und ein Teil davon war, dass der Actor nicht mehr Namen für die Grafiken hat, sondern direkt die Grafiken speichert.
das liegt u.a. daran, dass es zusammengesetzte grafiken sind und die Grafik nicht als einzelne Datei vorhanden ist.

bei der ganzen sache gehts also weniger um schutz, als um das speichern aller wichtigen Daten in einer datei.

insofern suche ich irgendwie nach einer anderen möglichkeit, eine Datei zu schreiben, sodass ich sowohl die Klassen, die bereits mit Marshall gespeichert werden können, als auch ein Bitmap in eine einzige datei zusammen rein speichern und dann auch als solche wieder auslesen kann.

7

Dienstag, 28. August 2007, 23:47

Gibt es zwei Punkte die dagegen sprechen ^^
1. Dein Savegame wird ziemlich groß wenn du da alle möglichen Grafiken reinpackst. Die haben da einfach nix drin zu suchen. Savegames sollen möglichst klein sein und nur die relevanten Daten enthalten

2. Es sollte eine strikte Trennung von Funktion und Optik im Maker geben. Diese Trennung wird mal mehr, mal weniger eingehalten, aber sie existiert und sollte auch gepflegt werden. Es gibt nunmal die Funktionsklasse Game_Actor und die grafische Klasse Sprite_Actor. Der Sinn dahinter: Die funktionale Klasse kann auch dann noch arbeiten, wenn die Grafik nicht benötigt wird.
In deinem Fall würde jedes Mal, wenn ein Held im Spiel vorkommt (ob er im Kampf kurz auftaucht, mal 'ne Zeit lang in der Party ist etc.) die Grafik für diesen geladen und behalten werden. An sich nicht mal sonderlich schlimm, dass Cache-Modul speichert ja auch alles was es kriegen kann. Aber es ist halt ein schlechter Stil.

Ich würde dir raten einfach dein Script entsprechend zu ändern. Es gibt imo keinen Grund weshalb die Grafik innerhalb des Game_Actor Objektes gespeichert sein sollte.
Bild
RMXP Grundkurs
1 2 3
Ruby/RGSS-Kurs

8

Mittwoch, 29. August 2007, 09:40

Hallo,
du hast ja gesagt dass die Grafik zusammengesetzt ist aus verschwiedenen Grafiken dann speichere im Helden doch im Array die Grafiekn aus dem es zusammengesetzt wird und dann holt sich Sprite_Actor bei der Anzeige das Array baut daraus das Bild zusammen und zeigt es an. Also so macxhe ich zumindest bei Chesudia und es läuft ganz gut :D (ok bei mir geht es immer über eval('C.i("bla","bla2","bla3")') .... )

Und wenn nicht nimmste ein bmp/png speicherscript auf rmxp.org schwirren die doch auch rum, musste dann nur aufpassen dass du dann Rechte in dem Ordner auch hast, wo d reinspeichern willst. Bei 98/Me ist das ja kein Problem da haste überall Rechte aber bei XP oder Vista kann es lustig werden. ;)

Gruß Sven
:information: YAams
:rainbow: Random Signatur
Bild





Weitere Informationen | Download
Mit Support für RGSS Project 1!
Bild

9

Mittwoch, 29. August 2007, 22:42

wenn es nur das zusammensetzen wäre, dann wär das ganze ja kein Problem

das problem bei der ganzen sache sind die Farbänderungen.
die Grafik wird aus unterschiedlichen schwarzweiß bildern zusammengesetzt, von denen der Spieler dann die jeweiligen Farben bestimmen darf.
das heißt, jedes mal, wenn die Grafik geladen wird, dann müsste man auch die Farben abändern.
das geht bei bitmaps nur mit get und set pixel.
da stirbt jeder Rechner von :P

aber mir ist grad ne lösung eingefallen.
ich mache eine Klasse, in der alle Grafikids und namen gespeichert werden und die eine Methode hat, die die grafik zusammensetzt und solange sie sich nicht verändert auch speichert.

nur gibts da glaub ich folgendes Problem (ich habs noch nicht ausprobiert, weil ich grad keinen maker hier hab)
dieses Objekt, das ich erstelle, sollte ja dann auch ein attribut es Game_Actor oder RPG::Actor sein
aber das müsste ja auch mitgespeichert werden.
das gibt ja auch wieder das selbe Problem, oder?

10

Donnerstag, 30. August 2007, 01:02

Abspeichern solltest du wie Abt Plouton gesagt hat nur die strukturellen Informationen der Grafik. Die Grafik selbst wird dann anhand dieser Informationen erzeugt.
Bei komplexen grafischen Prozessen solltest du die Ergebnisse cachen. Dafür gibt es ja das RPG::Cache-Modul, du musst es nur entsprechend erweitern.

Quellcode

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
#Ermöglicht es Objekte als Keys von Hashs zu verwenden
module Struct_Hash_Key
  def eql?(other)
    instance_variables.each do |var|
      sym = var.to_s
      break(false) unless instance_variable_get(sym).eql? other.instance_variable_get(sym)
    end
    true
  end
  def hash
    self.class.hash
  end
end
 
#Erweiterung des Caches
module RPG::Cache
  def add_gfx(key, bitmap)
    @cache[key] = bitmap
  end
  def get_gfx(key)
    @cache[key]
  end
end
 
#Grafikklasse
class Faerbbares_Charset
  include Struct_Hash_Key
  attr_accessor(:rot, :gruen, :blau, :charset)
  def to_bitmap
    #wenn Bitmap noch nicht erzeugt wurde
    if  bitmap=RPG::Cache.get_gfx(self) then
       bitmap
    else
      #Erzeuge das Bitmap
        bitmap = #....
      #Speichere Bitmap in den Cache
       RPG::Cache.add_gfx(self, bitmap)
       bitmap
    end
  end
end
Bild
RMXP Grundkurs
1 2 3
Ruby/RGSS-Kurs

11

Freitag, 7. September 2007, 14:46

also erstmal danke!
das mit dem Hash funktioniert ganz gut.

da gibt jetzt aber noch was:
und zwar gibts da ein Bild, dass ich nur aus dem Spriteset_Map generieren kann (ein Screenshot)

das Problem bei der ganzen Sache ist, dass das nicht soo schön ist, bei jedem laden ein neues Spriteset machen zu müssen.
deswegen hab ich mir gedacht, ich versuche das Bitmap zu komprimieren.

und meine Idee war folgende:

Spoiler: Idee

Quellcode

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
Class Bitmap
  def marshal_dump
    time = Time.now
    compressed = Compressed_Bitmap.new(self.width, self.height)
 
    for i in 0..self.width
      for j in 0..self.height
        px = self.get_pixel(i,j)
        index = compressed.pixel.index(px)
        if index.nil?
          compressed.pixel.push(px)
          compressed.positions[i,j] = compressed.pixel.size-1
        else
          compressed.positions[i,j] = index
        end
        time = antilag(time)
      end
    end
    return compressed
  end
 
  def marshal_load(comp_bmp)
    time = Time.now
    pixels = []
    bitmap = Bitmap.new(comp_bmp.width, comp_bmp.height)
    for i in 0..comp_bmp.pixel.size-1
      bit = Bitmap.new(1,1)
      bit.set_pixel(0,0,comp_bmp.pixel[i])
      pixels.push(bit)
    end
    for i in 0..comp_bmp.positions.xsize-1
      for j in 0..comp_bmp.positions.ysize-1
        bitmap.blt(i,j,pixels[comp_bmp.positions[i,j]], Rect.new(0,0,1,1))
        time = antilag(time)
      end
    end
    return bitmap
  end
 
  def antilag(time)
    if Time.now - time > 5
      Graphics.update
      return Time.now
    end
    return time
  end
end
zum Lesen den Text mit der Maus markieren


damit verkleinere ich die Anzahl der zu speichernden Pixel erheblich.
und das Laden des Bitmaps ist "relativ" Performance Schonend wegen blt
die Datei hat eine Größe von 1,6 MB.

ein Problem kommt jedoch beim Laden auf:
das Bitmap wird beim Laden disposed und ich kann es nicht mehr malen.

wie kann man das verhindern?

und wenn jemand noch mehr Ideen hat, wie man ein Bitmap komprimieren kann, immer her damit
ich hab mir nämlich gedacht, dass man das auch mit einigen weiteren extras erweitern kann^^

ich hoffe mal, einer von euch kann mir helfen^^

xelax90

12

Freitag, 7. September 2007, 17:11

Hallo,
ich zwar jetzt nicht was für ein Screenshotscript du hast, aber speichere es doch als jpg oder png, dann wird es doch automatisch komprimiert, dann musst du es nicht mehr manaul machen ;)

Gruß Sven
:information: YAams
:rainbow: Random Signatur
Bild





Weitere Informationen | Download
Mit Support für RGSS Project 1!
Bild

13

Freitag, 7. September 2007, 21:44

Du darfst innerhalb von marshal_load kein Bitmap.new() aufrufen, sondern du musst initialize() direkt aufrufen und auch sonst mit self.blt etc. arbeiten.

Allerdings funktioniert dein Code so ohnehin noch nicht. Du musst einen String abspeichern, der die Informationen des Bitmaps enthält. Also alle Pixel letztlich iterieren und in eine Textdatei speichern, wo sie dann wieder ausgelesen werden. Einfacher ist es wahrscheinlich doch, sich eine Library zu suchen, die pngs abspeichern kann.
Bild
RMXP Grundkurs
1 2 3
Ruby/RGSS-Kurs

14

Freitag, 7. September 2007, 23:57

meines Wissens kann man array auch seralisieren.
deswegen habe ich das so gacht

Die Klasse Compressed_Bitmap ist übrigens so difiniert:

Quellcode

1
2
3
4
5
6
7
8
9
10
11
12
class Compressed_Bitmap
  attr_accessor :pixel
  attr_accessor :positions
  attr_reader :width
  attr_reader :height
  def initialize(width, height)
    @height = height
    @width = width
    @positions = []
    @pixel = []
  end
end


soweit hatte ich ja auch keine Probleme mit dem Serialisieren.
deswegen wundert mich das auch, dass es beim Lesen Probleme gibt.

würde es denn keine Probleme geben, wenn man das Bitmap selbst erst später aus dem Compressed_Bitmap generieren würde?

oder müsste ich ohnehin den Array erst in einen string verwandeln und dann aus dem String auslesen und dann erst alles generieren?

15

Samstag, 8. September 2007, 05:40

Zitat

meines Wissens kann man array auch seralisieren.

Offenbar wird das zurückgegebene Objekt ebenfalls serialisiert (also 'ne Art rekursiver Mechanismus), solange bis schließlich eine IO-Instanz rauskommt. Aber ich dachte auch, dein Compressed_Bitmap wäre eine Subklasse von Bitmap, das ergäbe dann nämlich eine Endlosschleife ^^

Aber da dem nicht so ist, dürfte die Fehlerbehebung ganz einfach sein. Probier mal folgendes:

Quellcode

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Bitmap
  def marshal_load(comp_bmp)
    (avoid_hanging= Thread.new do
      loop do sleep(5); Graphics.update; end
    end).run
    initialize(comp_bmp.width, comp_bmp.height)
    comp_bmp.positions.xsize.times do |x|
      comp_bmp.positions.ysize.times do |y|
        set_pixel(x,y,comp_bmp.pixel[comp_bmp.positions[x,y]])
      end
    end 
    avoid_hanging.exit
  end
end


Die Sache mit dem Thread find' ich persönlich besser, als jeden Pixel die Systemzeit neu einzulesen. Und warum du für jeden Pixel nochmal ein neues Bitmap erzeugst (und dieses nicht disposed!) verstehe ich auch nicht so recht. Wichtig ist aber, dass du dich bei der Methode auf self beziehst, statt auf Bitmap.new

Achja, bei deiner Compressed_Bitmap-Klasse muss
@positions = Table.new(width-1, height-1)
stehen! Das ist kein Array
Bild
RMXP Grundkurs
1 2 3
Ruby/RGSS-Kurs

16

Samstag, 8. September 2007, 18:25

die Bitmaps hab ich erzeugt, damit ich nicht jedes Pixel mit set_pixel setzen muss, sondern blt benutzen kann

ich denke, das ist effizienter.

aber ich verstehe grad, wo das Problem lag^^
thx

ich werd mal beide methoden versuchen und mal gucken, was schneller geht^^


ps
du glaubst nicht, wie lange ich nach einer sleep funktion gesucht hab :P

Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von »xelax90« (8. September 2007, 18:35)


17

Samstag, 8. September 2007, 20:34

Du verwendest doch so oder so set_pixel. Du erzeugst in deinem Code ein Bitmap, setzt einen Pixel und blittest das Bitmap dann auf ein anderes Bitmap. Ich glaube den Pixel direkt auf ein anderes Bitmap zu setzen erspart dir einen Zwischenschritt ^^ Aber probier einfach mal aus, was schneller ist.

sleep() klingt btw. toller als es ist. Für Threads kannst du es benutzen, der Hauptthread allerdings darf nicht mit sleep angehalten werden, da sich sonst das ganze Spiel festfährt (der Spieler kann dann nicht mal das Spiel wie gewohnt verlassen etc.). Da ist Graphics.update einfach die bessere Lösung.
Bild
RMXP Grundkurs
1 2 3
Ruby/RGSS-Kurs

18

Sonntag, 9. September 2007, 14:55

jaja, ich weiß schon, dass man sleep nicht immer verwenden darf.
nur manchmal ist das schon noch wichtig, weil man da nicht drum rum kommt^^
bis jetzt hab ich immer die variante mit dem Time benutzt^^

zu dem wieder zusammensetzen:
der Unterschied ist irgendwie nicht bemerkbar^^

das Problem ist, dass beides recht langsam geht^^
also man braucht für die Grafik, die ich habe (200x160px) ca 3 bis 5 sec.
das ist doch recht lang für eine Grafik, wenn man es mehrmals hintereinander aufrufbar machen will.

ich versuche mich mal mit dem jpeg algorithmus vertraut zu machen
vlt. kann man damit was anfangen.
aber weißt du vlt. noch ne andere möglichkeit?
(außer es in einer externen Datei abzulagern versteht sich^^)

Social Bookmarks