Jump to content
Unity Insider Forum

Blogs

Featured Entries

  • Mark

    UltraTerrain Dev Diary #2

    By Mark

    Nachdem die Tests nun immer umfangreicher werden wächst auch die Funktionsvielfalt.

    Im letzten Post habe ich den PageDataHandler erwähnt, dieser ist nun fast vollständig implementiert, dazu musste aber einiges gemacht werden.

    Der PageDataHandler soll wie der Name bereits andeutet in der Lage sein die Daten einer Page zu handhaben/verwalten, darunter fällt wie bereits erwähnt das Laden der Daten.

    Das Laden der Daten basiert auf 2 Schritten:

    1. Wenn noch keine komprimierten (zip) Daten im Objekt vorhanden sind dann wird das dem System zugehörige VirtuelleFileSystem nach den passenden Page Daten gefragt. Das VirtuelleFilesystem ist das VFS aus SaveIt und kann in einer Datei verschiedene Unterdateien speichern und lesen, das ganze ohne jedesmal die gesamte Datei in den Speicher zu laden. Die Daten wurden vorher komprimiert in das VFS abgelegt.

    2. Sind die komprimierten Daten vorhanden werden diese dekomprimiert und in die Datenarrays gelegt die ein PageDataHandler hat. Das sind aktuell nur 2 Arrays, einmal die VoxelValues welche angeben wir einflussreich ein Voxel ist (0.0 bedeutet kein VoxelEinfluss und 1.0 bedeutet vollen Einfluss) und die BlendValues, welche angeben wie die verschiedenen Texturen später miteinander verschmelen werden.

    Der 2. Schritt ist gesondert vom ersten aus folgendem Grund: Der PageDataHandler soll wenn eine Page längere Zeit unverändert bleibt die Daten zwischenspeichern können um Speicherplatz zu sparen um später wenn die Page noch viel länger unangetastet blieb diese Daten in das VFS zu speichern. Dies soll später dafür sorgen dass nur die Voxeldaten in Speicher verbleiben die unbedingt nötig sind um entweder schnelle änderungen zu ermöglichen oder wieder schnell parat zu sein um änderungen vorzunehmen.

    Intern arbeiten die Load und LowerMemorConsumption Methoden mit Asynchronen Operationen die jeder Zeit abgebrochen werden können um zB wärend eine Page grade die Daten komprimiert diese Operation abzubrechen da ich die Daten spontan doch unkomprimiert benötige.

    Das System für diese Asynchronen Operationen basiert auf den Unity Threading Helper.

    Bestimmte Aktionen können allerdings nicht abgebrochen werden und sind damit auch synchron und nicht asynchron, zB das löschen aller Daten bei dem es keinen Zwischenschritt gibt der noch abgebrochen werden könnte.

    Das System zu laden und Speichern der VoxelDaten bedarf aber noch einen weiteren Zwischenschritt um Versionsupdates auf neuere UltraTerrain Versionen zu ermöglichen, aus diesem Grund wird in den Page Daten später noch die Version des Dateiformates (unabhängig vom VFS Format) abgespeichert werden und basierend auf dieser Version werden dann die geeignetesten Speichermethoden gewählt.
    • 0 comments
    • 466 views
 

Mr.Clowns Spielchen [TAG 2]

INTRO
Nachdem ich so große Sprüche gemacht habe (in meinem letzten Blogeintrag), muss ich euch ja nun auch was liefern. Da ich gestern bereits das vierte Mal durch mein Script gegangen bin und die anderen schweren Prüfungen noch locker drei Wochen entfernt sind, erlaube ich es mir mal ein wenig mehr an meinem Blog zu schreiben. DIE IDEE
Wo soll ich anfangen? Kurz nach meinem Kackblogeintrag habe ich mich im Bett (zwei Stühle mit einer Decke) herumgeweltzt und nachgedacht. Eigentlich wollte ich mein eigentliches Spiel (an dem ich arbeite) herannehmen aber da habe ich übelste Paranoia. Also musste etwas anderes herhalten. Das kennt bestimmt jeder von euch. Also, wie habe ich das angefangen? Mein Plan war einfach: Ich habe mir Spiele angeschaut, die man bereits kaufen kann und ihre Besonderheiten näher betrachtet. Für eine One-Man-Show muss das Projekt klein und überschaubar bleiben, sonst wird das nichts.
Geometry Wars Evolved²
Das fand ich recht interessant. Es ist klein, schnell, kurz und macht einigermaßen Spaß. Die Grafik ist auch recht nett. Also habe ich es mir zu Testzwecken gekauft. Naja ...

Dann habe ich noch ein paar andere eigenartige Spiele entdeckt, die ein paar Nerds bei uns im Computerpool an der Uni gespielt haben. Es war eine Art Snake ... aber man konnte das im Multiplayer spielen und die Regeln waren anders. Die Snake wurde immer länger mit der Zeit und die Spieler konnten spezielle Items einsammeln, um über gegnerische Snakes zu hüpfen.

Dann hatte ich noch so etwas wie Minecraft im Sinn ... aber alles zu overpowered oder kaum 1:1.5 kopierbar (man kann also kaum etwas ändern ohne dass jeder das Original darin erkennt). Nachdem ich mich also ein wenig umgeschaut habe, dachte ich dann über Spiele nach, die ich bereits kannte und von denen eventuell ein Remake gut machbar wäre. Aber auch die alten Nin10Do Klassiker sind einfach zu aufwendig. Letzten Endes habe ich mich entschieden einfach ein Remake dessen zu machen, was unser Team damals an der FH abgegeben hat. Ein Survival Spiel mit Zombies ...  wer hat das kommen sehen? So ziemlich jeder, würde ich behaupten. WARUM SURVIVAL?
Ich würde ja gerne Bilder und so hier hochladen, aber leider habe ich entweder kaum Platz oder ich muss meine Bilder irgendwo auf komischen Seiten uppen. No thanks. Vielleicht finde ich einen anderen Weg. Jedenfalls ... Survival Spiel ... gibt es wie Kakerlaken in meiner Wohnung (oder Sand am Meer, wem es gefällt). Aber ich möchte euch kurz erläutern, warum mir die jetzigen Survival Games tierisch auf den Sack gehen und wodurch unser Spiel sich damals unterschieden hat. Fragt euch mal: Ist es wichtig, ob es Zombies oder Aliens sind? Entscheidet die Spielfigur wirklich über das gesamte Spiel? Stellt euch einfach ein gutes Spiel vor und ersetzt die Figuren dort durch andere Figuren aus einem anderen Genre ... macht es das Spiel an sich kaputt? Jain. Meiner Meinung nach liegt es am Detail und dem Flair, also der Atmosphäre, die durch die Figuren erzeugt wird. Stellt euch mal vor, wie es wohl wäre, wenn in Dead Space statt dem KFZ-Mechatroniker Isaac ein Steuerberater mit zwei Köpfen gewesen wäre. Mir würde es gefallen, aber dem Mainstream wohl kaum und wenn einer durch ein Spiel reich werden will, muss er sich leider dem dummen Mainstream beugen. Ja, beugt euch ganz tief ... *schmatz schmatz* Kennt einer das SNES Spiel "Zombies"? Da gab es Zombies UND Aliens ... das hat mich damals so geflashed, dass ich selbst heute noch manchmal ausflippe (z.B. bei Beerdigungen oder Hochzeiten ... wobei ich selbst keinen Unterschied in beidem sehe). BESCHREIBUNG DER IDEE
Survival Spiel. Der erste Teil in diesem Video ist ungefähr das, was ich mir auch damals bei unserem Projekt vorgestellt hatte: Video Mir ging es eigentlich um die Aktionsmöglichkeiten in einem solchen Spiel, denn Survival Spiele in Form von Shootern gibt es ja mehr als genug, da wird eher das 2000jährige Reich ausgerufen (das nur halb so lang halten wird wie das 1000jährige), als dass jemand ein NON-SHOOTER Survival rausbringt. ÜBERLEBEN STATT TÖTEN war damals mein Motto. Ich wollte ein Survival bei dem man wirklich nur versucht zu überleben anstatt mit Waffen etc. andere zu töten. So stellte ich mir vor, dass man Nahrung und Trinken suchen musste und versuchen sollte sich durch die Gegend durchzuschleichen, ohne aufzufallen. Stellt euch eine dunkle Stadt vor, in der einige Häuser brennen und manche Menschen liegen heulend auf dem Boden. Das Blut läuft die Straßen runter. Rauch verschleiert die Sicht. Man hört viele Geräusche: Schreie, Schluchzen und Hoppelhäschengebrüll (Hasen brüllen doch, oder?). Deine Figur versteckt sich unter einem Bett und wartet, bis es Tag wird. Doch plötzlich läuft das Wasser über den Rand der Badewanne und verursacht in einer Steckdose einen Kabelbrand (Feuerwasser eben  ). Ihr müsst also raus ... packt euren Rucksack, nehmt euren Gürtel und raus aus dem brennenden Haus. Ich wollte eine Welt aufbauen, in der alles aus Blöcken etc.. Die Aktionen des Spielers sollten nur auf Überleben getrimmt werden. Klar kann man auch kämpfen, aber niemals töten. Die Ausrüstung sollte nur auf den Rucksack und den Gürtel beschränkt werden ... es könnte auch eine Gasmaske geben. Egal. Hier ein kurzes Bild:

Hier sei klar erwähnt, dass ich dieses tolle Bild nicht auf Google wiederfinden will. 
Die Gegend sollte der Hauptinteraktionsakteur sein. So wollte ich folgendes ermöglichen: Verlegung von Leitungen (Wasserrohre und Stromleitungen) sodass man Geräte verbinden und funktionstüchtig machen konnte. Wasser könnte umgeleitet werden etc. Effekte wie Feuer und Kälte realisieren. Sodass Leute, die zur Hälfte im Wasser stehen und sich in einer Gefrierkammer befinden, eben in diesem Eis stecken bleiben. Auch Rauch sollte seinen Nutzen erhalten. Wenn jemand im Rauch ist, wird die Sicht für den Spieler komplett nur auf den Rauch reduziert.
Glas sollte dem Spieler schaden etc. Feuer schmilzt Eis und damit wird Wasser freigesetzt ... dieses wirkt sich auf die Umgebung aus. ...
Was die Figur betrifft, so hatte ich sowas im Sinn: Figur muss essen und trinken. Wenn sie zu wenig isst, dann wird sie schwächer, langsamer. Bei Durst treten Halluzinationen auf etc. Figur kann in Panik geraten, wenn der Puls ansteigt (hier wollte ich über Kinect oder was anderes den Spieler mit einem Pulsmessgerät ausrüsten ... wäre bei Dead Space bestimmt witzig  ) Figur kann schleichen, geduckt sein, rennen, schneller rennen (mit quickevents ... bei Fail stolpert die Figur), stolpern, springen, klettern ... habe mir mal einen Zustandsautomaten aufgemalt, aber gerade nicht parat. Figur kann mit anderen Figuren interagieren wie schubsen, schlagen, festhalten etc. Figur hat Eigenschaften wie Moral etc.. Wie genau ist das gemeint? Man wählt (durch einen Test), was für eine Moral man hat: Gut, Mittel, Böse etc.. Jede Aktion im Spiel hat ebenfalls einen moralischen Wert (durch durchschnittliche Werte ermittelt) und wenn deine Aktionen von deiner Moral abweichen, wirkt es sich negativ auf deinen Figur aus. Panik etc. sind dann die Folgen. Figur kann Gegenstände auch in andere Objekte packen: Eine Bombe in einen Koffer und den Koffer in einen Schrank. Das wird bestimmt witzig, wenn alle sich im Raum verstecken.  Ich habe hier ein paar wenige Elemente aus meinem eigentlichen Spiel reingebracht. Aber ihr erkennt, dass es auch Elemente sind, die so eigentlich nirgends vorkommen und/oder keine Rolle spielen. Moral z.B. ... WAS IST DAS ZIEL
Man muss so lange überleben, bis Hilfe eintrifft (in Form eines Teams, das evakuiert). Ganz einfach eigentlich. Im Verlauf des Spiels ereignen sich immer mehr und stärkere Dinge wie: Erdbeben, Meteoritenschauer etc. Alles generisch, sodass man eine Zufallsmap kreieren kann ... für den Wiederspielwert(?). Kein Bock weiter zu schreiben ... denkt nach, kommentiert und dann mache ich irgendwann weiter.

Mr. Clown

Mr. Clown

 

Mr.Clowns Spielchen [TAG 1]

EINLEITUNG:
Hallo ihr. Ich lese oft in Foren, was für tolle Ideen irgendwelche Leute haben und wie toll sie sich das alles vorstellen. Dann fängt es immer so an, dass man sofort loslegt und irgendetwas programmieren will. Tja ... klar, man hatte eine gute "Idee" oder wie ich es öfters bezeichne (mit öfters meine ich: ziemlich selten oder noch nie) "Pseudofuck". Was bedeutet Pseudofuck? Das bedeutet, dass man eine gute Idee bezüglich eines einzigen Elements des gesamten Spiels hat und dass das Gehirn uns vormacht, als sei es bereits das fertige Produkt. Z.B.: <<Hammergeile Idee! Werwölfe vs. Zombies! Mit so voll geiler Grafik! Mit Licht und so Schatten!>> Man hat für gewöhnlich ein konkretes Bild vor Augen, aber wenn man genau darüber nachdenkt, so handelt es sich eigentlich nur um Plagiate ... alle Elemente in der Vorstellung stammen von irgendwo anders. Und aus einem kleinen Snapshot schließt man auf das große Ganze.
Nachdem man dann ein Element der Idee realisiert hat, merkt man, dass man eigentlich nichts hat ... und dann versucht man den Haufen Müll, den man bisher erreicht hat, durch Erweiterungen (manche kennen das von DLCs) zu verbessern ...  AUFGABE:
Was ist meine Aufgabe? Ich werde die Scheiße aus euch prügeln. Ich werde dafür sorgen, dass euch das Entwickeln der Spiele keinen Spaß mehr macht und ihr euch nur noch den Tod wünscht (für mich ist es gleichbedeutend mit XCOM EU 2012 zu spielen). Dazu gehören Dinge wie: Omg! Ich habe eine Idee ... glaub' ich ... was jetzt? Anforderungsanalyse Das besondere Etwas Leveldesign Charakterdesign KI Inventarsysteme etc. Natürlich werde ich meinen Schwerpunkt teilweise auch auf neue Konzepte legen und nicht einfach Kopien bereits existierender Spiele machen ... das gibt es ja alles schon ... aber es gibt noch einiges zu tun ... und meine Seminararbeit wäre eventuell eine gute Idee für Spiele etc.. (Thema: Incremental Dialogue System) Was berechtigt mich das zu tun?
Naja, ich betrachte die Spiele für gewöhnlich wesentlich kritischer, als meine dummen Freunde (jeder hat 2 Wochen Zeit das Land zu verlassen, sobald ich Reichskanzler werde).
Außerdem habe ich bereits ein verteiltes Spiel (als Projekt an der FH damals) entwickelt und habe so einige Dinge von anderen Gruppen mitbekommen (wie man es nicht machen sollte).
Weiterhin entwickle ich auch Programme für meinen eigenen Bedarf ... Klar, ich bin kein Profi und ich habe mit Unity noch kaum etwas getan. Aber ich will euch auch nur Tipps und Konzepte vorstellen (auch wenn die nicht unbedingt top sind). Wir haben damals bei unserem Spiel auch eine Menge "Fehlentscheidungen" gemacht und viele Dinge einfach falsch bewertet (als unwichtig oder easy2go  ). Aber das, was ich in den Foren so finde ist wesentlich schlimmer, als das, was ich euch zeigen werde und somit gehe ich reines Gewissens an die Sache ran. HINWEIS:
Wie oft und wie intensiv ich an diesem Blog schreiben werde wird sich noch zeigen müssen, da ich demnächst 1000 Klausuren schreiben muss und eine Seminararbeit abgeben muss, an der zwar 3 Leute arbeiten, ich aber die 80% liefern muss ... weil keiner einen Plan davon hat und ich mein "Team" (oder wie ich die nenne: die Dauereakulierer) nicht aussuchen konnte.  Boom! Wenn ich durchfallen sollte, dann ... tja ...  ... kommt davon, wenn man als Freimodul ein Fach wählt, das nichts mit dem Studiengang zu tun hat.  Dafür kann ich Menschen teilweise reparieren. FRAGE:
Was ich noch nicht weiß ist, ob ich den Interessierten unter euch (also niemandem) immer Aufgaben stellen sollte, die ihr abgeben müsst, oder ihr einfach mir Ideen zu dem Spiel schickt (welches wir entwickeln werden) ... dann kann ich die Ideen als Umfrage realisieren etc..

Mr. Clown

Mr. Clown

 

XCOM EU (2012)

XCOM EU (2012) Ihr kennt es, ihr mögt es eventuell ... und wenn ihr es mögt, dann hasse ich euch. Ich kenne die alten Teile und mochte diese auch. Habe wegen dem Spoilerzeug damals extra keine Tests und Reviews von Xcom EU 2012 angeschaut ... ein großer Fehler. Publisher: "Wir müssen ein Spiel rausbringen, aber so, dass wir nahezu nichts dafür tun müssen."
Entwickler: "Machen wir doch ein Remake eines bekannten Spiels!"
Publisher: "Tolle Idee! Schon ein Spiel im Sinn?"
Entwickler: "Ja! Nehmen wir XCOM."
Publisher: "WOW! Das hört sich schon mal ganz gut an."
Entwickler: "Ja! Der Name zählt!"
Publisher: "Wie stellen sie sich das Remake vor?"
Entwickler: "Ja ... ich habe das Original zwar nie gesehen oder gespielt, aber ich glaube, dass das Basissystem und Inventar, sowohl die KI nur nebensächlich waren. Es ging da nur um die Action gegen Aliens."
Publisher: "Hört sich gut an! Holen sie sich ein paar Werkstudenten und entwickeln sie das Remake!"
Entwickler:
Ich bei dem Spiel, nach den ersten paar Minuten:
Ich hatte eigentlich vor noch mehr in sinnvoller Art und Weise zu schreiben, jedoch weiß ich einfach nicht, wo ich mit der Kritik anfangen sollte ... also werde ich meinen E-Mail-Verkehr mit meinen Freunden publizieren ... da drin findet ihr eine "nette" Diskussion. AKT 1 :: Der letzte AKT
ICH
hm strange .... mien scharfschütze steht 4m vom gegner weg ... kenie hindernisse und keine deckung. nur 70% treffsicherheit .... und er verfehlt. ps, wieso kann man eine mission nicht einfach neustarten? wieso muss ich erst den spielstand aus der base laden und dann die mission neu anklicken? schein mir nicht sonderlich durchdacht zu sein. ach ja ... und die gegner treffen meine leute auch, obwohl ich: hinter einer wand bin hinter einem auto etc. da ist garkeine sichtlinie. wtf? wie beim sniper ... der hat ja die fähigkeit auch ziele anzugreifen, die er selbst nicht sieht, aber ein verbündeter ... wie soll sowas gehen? ich kann vom arsch der welt auf eine einheit feuern, die ich nicht sehen kann und die hinter 1Mio deckungen ist ... habe durch das gesamte gebäude (wände etc.) geschossen und den typen erwischt. das hat doch mit tatktik nichts mehr zu tun. aber ich spiele mal weiter ... schlimmer als JA3 kann es ja nicht werden.
  FREUND NR.1
Je weiter der Gegner weg ist desto höher ist die Treffsicherheit beim Scharfschütze, und er braucht immer noch eine Sichtlinie … also wenn du hinter einer Wand den Gegner siehst kann er nicht durschießen …. Und wenn du dich an eine Wand anlehnst bist du nicht vollständig hinter einer Deckung sondern nur halb …. Wir können ja mal zusammen im Multiplayer Spielen dann zeig ich dir die Unterschiede die ich meine FREUND NR.2
Der Scharfschütze verliert an treffsicherheit je näher du an dem gegner bist. Da switche lieber zur handgun.
Also auf xbox kann man wärend der mission laden und speichern. Ich hab das immer gemacht um keine männer zu verlieren. Sonst kannste das echt vergessen.
Ingsgesamt macht das spiel schon laune, aber wie du gesagt hast, es hat nichts mit richtiger taktik zu tun, zumal dich die gegner immer gleich sehen, wenn du sie siehst. kein anschleichen etc. aber das hab ich ja schon alles erwähnt. Das wichtigste ist es niemals das zu forschen was die von dir wollen, sonst kommst du zu schnell in der story voran und hast null nützliches equipment. also immer genau das gegenteil machen was die von dir erwarten. "Fange einen auserirdischen" mach das bloß nicht, sonst sind in der nächsten mission 5 von der sorte am start. erstmal waffen forschen und männer hochleveln. ICH
seit wann muss man weit weg sein, damit einen der scharfschütze gut
treffen kann? das mit der halben deckung ist ja klar ... mich irritiert nur,
dass wenn man komplett hinter einer wand steht, dass man dann trotzdem
getroffen werden kann: ein scharfschütze wird man ja nur, wenn man mit normalen waffen bereits
hammergeil umgehen kann. also ... im ernst ... wenn du ein
scharfschützengewehr hast und einer steht 5m vor dir ... die trefferwahrscheinlichkeit ist
doch dann 100% ... was mich noch tierisch aufregt ist: wenn man bestimmte punkte bei einer
mission erreicht, dass dann gegner mitten im gebiet spawnen. ich habe diese
entschärfungsmission gemacht ... einer hat es bis zur bombe geschafft und
sie entschärft ... und auf einmal sind diese mensche/aliens aufgetaucht ...
genau um die bombe herum ... und gleich mit feindkontakt (diesem
augensymbol, wo sie gleich schießen, wenn sich einer bewegt). man kann ja nur
einmal schießen ... was soll man da machen? 3 on 1 und natürlich stehen
die total ungünstig. mein ganzes team wurde zerfetzt. auch dumm:
wieso gerät das team in panik, wenn nur ein kollege stirbt? das sind doch
soldaten. was soll der dreck? einer ist verreckt ... ich schicke medi los.
der ist am heilen ... er braucht nur noch eine runde zum healen ... und
was passiert? genau in der letzten runde bekommt der medi panik und haut ab.
der andere stirbt und ein weiterer kriegt panik. panik ist ja okay, aber
ich finde es hier zu billilg gemacht. außerdem, wer schickt bei einer alien invasion nur 5 leute ins
kampfgeschehen? iwas war noch .... etwas ganz dummes, habe aber gerade vergessen.
  FREUND NR.1
Das kann aber auch nach hinten losgehen ;D alter schau auf die Werte deiner Soldaten .... je mehr Wille die haben desto weniger schnell geraten die in Panik. Jede kritische Verwundung senkt Wille permanent. Und ein Scharfschütze mit einem Scharfschützengewehr mit 12X zoom braucht ne ewigkeit und einen Gegner in 10m Entfernung zu treffen..... Deswegen rennst du auch nicht mit nem Scharfschützengewehrrum um Gebäude zu stürmen und wenn du kommplett hinter einer Wand stehst kannst du nicht getroffen werden. Komplett hinter der Wand bedeutet links und rechts von dir muss ein Feld mit voller Deckung sein, sollte er dich immer noch Treffen können ist es ein bug. Das spawnen ist nervig aber man gewöhnt sich dran, tritt eh nur bei allen Ratseinsätzen auf und du kennst die spawnpunkte irgendwann auswendig ;D und du bist auf max 7 limitiert weil du im spätern verlauf sonst übermächtig wärst. Dann müssten deine Rekruten noch schlechter sein ICH
mir ist es wieder eingefallen. wenn man saved und runde beendet verläuft das spiel nach schema f. wenn
man aber den spielstand wieder lädt, kommen dinge hinzu, die davor nicht da
waren. bei mir war es so, das nach dem laden die chars alle in panik gerieten.
egal wie oft ich geladen habe. ohne laden, als ich normal weitergespielt
habe, geschah das nicht. und FREUND NR.2 , wenn man immer lädt, weil chars sterben, ist es doch lame ...
mit toten ist es viel funnier. ich mach uns alle als chars rein ... ^^
  FREUND NR.2
Ne, meine Chars waren zu episch um zu sterben. Ich schau grad die Harry Potter filme an. Da kommt voll oft ne Handlung vor, die ich persöhnlich unfair finde. Ich frage moch ob Kinder das mögen und nur erwachsene nicht. Oder ob es an mir liegt, dass ich ein ausgeprägtes Gerechtigkeitsempfinden habe. Bsp: Da Gewinnt Slitherin die meisten Punkte und wird zum Gewinner ernannt. Dann kommt Dumbledore und meint: Hey jo, Harry war voll cool drauf, also bekommt Grifindor nochmal +50 Punkte womit Grifindor nun Gewinner ist und dann jubeln alle. Das ist doch voll unfair. Glaubt ihr da freuen sich die Kinder mit Harry?
  FREUND NR.1
du hast bestimmt auf nornal gezockt *gg* und ja die Harry Filme sind da etwas merkwürdig ICH
das ist das alte traurige lied von "kindegerechten" geschichten. es muss ganz klar gut und böse geben ... und gut wird immer belohnt. natürlich ist die realität anders ... tugend wird bestraft und asoziales verhalten bringt einen weiter. ich finde die harry potter filme zu lame. ... die kulisse ist zu eintönig und es ist irgendwie immer dunkel und die machen dauernd so blöd. ich meine ... die steht auf ihn und er auf sie und die sind doch wie alt? 20? tun so als wären die so 8 jahre alt im sinne von:
X: ich mag harry ... hihihi ... ich trau mich aber nicht das zu sagen
Y: ich mag dumbeldoom (und wenn er auftaucht, schaut Y ganz verlegen auf den boden)
was für ein fuck. ich weiß nicht, warum slitherin als böse dargestellt wird. nur weil die eine schlange als symbol haben? und was bitte schön ist schwarze magie? jede art der magie ist einfach nur magie. oder gibt es auch schwarze informatik oder schwarze elektrotechnik? zum glück kommt so ein scheiß nicht in meiner geschichte vor. da gibt es weder gut noch böse ... nur persönliche nutzen. xcom ist schon noch buggy. habe gerade eine granate geworfen und beim klick ist die kamera unter die erde geflogen und dann im kreis um die karte.
wieso wird beim spielladen nicht angezeigt in welcher runde man wra? den levelnamen zu kennen und das datum ist ja voll nutzlos. ein aktueller spielstand (vom datum her) kann ja rundentechnisch trotzdem uralt sien. iwie machen die bei all den games immer die selben fehler. habe schon 2 nationen verloren ... kriegt man die wieder?
ach ja ...
6 einheiten von mir vs. 1 enemy. min 74% max 85% treffsicherheit ... alle verfehlen. ich finde auch, dass wenn man sich nicht bewegt, dass man dann zwei mal schießen können sollte. ist doch behindert, wenn 6 leute von 12 umzingelt sind ... kann man ja max 6 abknallen ... und danach beginnt das große sterben. ich fand in apocalypse haben sie das problem mit den overpowerten units gut gelöst. die haben einfach beiden seiten eine waffe gegeben, die fast alles, was man zuvor erforscht hatte, wertlos macht. diesen enzym-teil ... wo dann die rüstung und alles andere zerlegt hat. ohne teleporter war man voll aufgeschmissen. so sollte es gemacht werden und nicht einfach enemy.HP += 200. ps, wenn ich nun nur mit rekruten in eine mission gehe ... werden die aliens wieder nach unten angepasst oder ist es dann impossible wie bei aftermath? bei aftermath haben sich die aliens an der stärksten spielereinheit orientiert ... dies hatte zur folge, dass man nur sien anfangsteam nutzen konnte. die rekruten sind schon beim ersten einsatz verreckt wie die fliegen.
  FREUND NR.2
Deswegen wird dein Buch auch ein flopp. Du musst dich an den Siegern orientieren.
Die gegner richten sich an deinem Fortschrittslevel in der Hauptmission. So hab ich das zumindest erlebt. Bevor du beispielsweise keinen Outsider fängst wirst du niemals auf einen ethereal stoßen.
  FREUND NR.1
Die werden nicht nach unten angepasst wenn du mit Rekruten kommst wirst du sterben ;D gegen später zumindest .... und der scharfschütze kann z.B 2 gegen später schießen hängt immer davon ab was du bei den leuten skillst
Ignorier die Aussage von FREUND NR. 2 sie ist Falsch ;D. Wenn du zu lange brauchst kämpfst du gegen die härtesten Gegner mit Krüppelequip und Fortschritt 1 in der Hauptmission ;D FREUND NR.2
Klar hab ich recht. Beim ersten mal zocken hab ich voll abgeloost weil die übelst harten gegener gleich am start waren. Beim zweiten versuch hab ich ich erstmal maximum erforscht und so viele sateliten wie möglich geholt und erst dann in den missionen weitergespielt. Hab dann vom Rat immer die Bestnote bekommen und nie ein land verloren. Da kommt es dann auch vor, dass überhaupt keine Missionen mehr zu machen sind bis du irgendwas erforscht hast. Da kannste fett kohle vom Rat absahnen und die Monate durchlaufen lassen ohne das was passiert.
  ICH
das mit den rekruten ist ja mal gay. so ein übler fuck. apocalypse war echt besser ... ich schnall nicht, warum man nicht einfach ein 1:1 remake von apocalypse oder terror from the deep macht? nur die grafik anpassen, alles andere lassen wie es ist. ist das so schwer? ich finde allgemein ... wenn von einem spiel ein weiterer teil erscheint, hat es oft nichts mehr mit dem alten zu tun oder ist 1:1 der vorgänger. siehe anno reihe oder heroes of m&m. hom&m fand ich 3 am besten und 5 okay. von dem neuen bin ich momentan nicht so begeistert. die karte wirkt zu leer ... einige objekte wiederholen sich. vor allem levelübergreifend. man findet fast immer den selben wasserfall, nur an einer anderen stelle. die hätten schon so 3~4 modelle dafür erstellen können. anno ist ja wohl der größte beschiss. 2070 ist 1:1 1404. sogar die selben bugs ... und die extras die im nachhinein gekommen sind ... sind genauso wie in 1404. z.b. dieses statistikgebäude ... und die labern von noch nie dagewesenen verbesserungen. so ein scheißdreck. hm ... ja ... meine story wird wohl nicht kindergerecht sein ... damit ist der markt so ziemlich leer ... mal sehen, ob ich es anpasse.
  FREUND NR.1
Es ist ein Remake von Enemy Unknown also von UFO weder von Apokalypse noch von Terror from the Deep von daher ist die argumentation fürn arsch und es ist so gut wie UFO
  ICH
o man ... ich dachte es geht hier um deckung und sowas ... aber jetzt kommen ja gegner hinzu, wo es latte ist, ob die in deckung sind oder nicht. das finde ich total lame ... wo bleibt hier bitte die taktik? dieser berserker ... der läuft einfach auf dich zu und du ballerst mit iwelcher beschisswaffe und kriegst ihn kaum down. habe zum glück mit der hauptmission gewartet und zumindest lasergewehr gebaut und plasmageweher erforscht. ich kann mir nicht vorstellen, wie man da mit einer normalen waffe weiterkommen soll. bis jetzt finde ich das spiel nur so lala ... es ist halt leider nicht wirklich taktik. meine taktik ist: sich iwo gut verschanzen und abzuwarten was passiert ... haut prima hin. die grafik ist schon fein und die details sind auch gut. aber es wirkt halt so, als ob man sich mehr gedanken um die grafik statt spielmechanik gemacht hat. z.b.: meine einheiten sterben nicht im feuer. ich kann die ins feuer stellen und denen passiert nichts. es leuchtet alles schön, aber das wars auch schon. wie es aussieht sind die levels auch handmade und nicht durch einen generator. nach einer bestimmten anzahl an missionen scheint sich die umgebung zu wiederholen. vor allem die eskortmissionen. ich verstehe nur noch nicht, wie das spiel 12GB schlucken kann. da ist ja nichts besonderes drin ... andere spiele mit besserer grafik haben kleineren bedarf. was macht also die 12GB aus? basisbau ist okay. wobei mehrere bases mir immernoch besser gefallen hätten ... und größere räume. man bekommt ja so boni, wenn gebäude nebeneinander gebaut werden ... aber iwie ist bei mir alles immer querbeetdurchgebaut. finde es auch nicht unbedingt balanced bei basisbau ... man baut ein paar sachen und schon braucht man ein neues kraftwerk ... ich wusste nicht, dass man für den betrieb von ein paar räumen 3~4 kraftwerke braucht. dann baue ich das kraftwerk und gleich danach brauch ich auf einmal ein satelliten kontrollzentrum ... und auf eins folgt das andere etc. bei so FF games hasse ich es übelst wenn man 89832472498 Hitpoints verursacht ... da denk ich mir: können die das nicht normal machen und beschränken? aber sind halt japaner ... die mögen große zahlen, weil sie so verfickte genies sind. aber bei xcom finde ich es nur komisch, dass ein jet 40$ kostet und ein rekrut 15$ ... ich rechne inzwischen in leichen. 3 leichen = 1 rekrut. 8 leichen = kraftwerk. (1 leiche = 5$) sagt mal, kommt da noch eine waffe, die nicht alles permanent pulverisiert? irgendwie wird bei mir dauernd die ausrüstung der aliens zerstört ... dabei nutze ich projektil und laserwaffen ... wie bitteschön soll eine pistole beim gegner das gewehr schrotten? einfach nicht durchdacht ... und es werden wohl auch keine weiteren patches kommen.
  FREUND NR.1
Die waffen gehen immer kaputtt außer du betäubst den gegner ;D und auch da hast du nur eine chanche dass sie nicht kaputt geht … der Radius vom Raketenwerfer ist halt so … war im alten teil auch nicht anders und finde ich auch gut so …. Wenn du willst das der schaden abnimmt nicht 2 welle mach random waffenschaden an und alles ist so wie es ist … da kannst dann auch mit ner plasmawaffe 1 Schade machen. Es gibt nur 4 Gegnertypen denen Deckung egal ist, also reg dich nicht auf … warte mal ab bis die gepanzerten Muten kommen, da hast du teilweise mit scharfschützen treffer wahrscheinlichkeiten von 24% wenn die hinter voller Deckung stehen. Hast du UFO überhaupt gespielt ??? Die Karten sind zu wenige da stimm ich dir zu aber ansonsten teile ich deine Meinung nicht im geringsten .. Schon alleine der Basisbau ist taktischer als in alle xcom Teilen davor … bau ich ne Werkstatt oder kauf ich lieber 4 Rekruten, bau ich schnell nach unten um ein Wärmekraftwerk zu bauen oder baue ich viele kleine. Auch die unterschiedlichen Klassen und deren Waffen bringen mehr Taktik rein als es in dein GOTT gelobten Apokalypse waren. In Apokalypse bist du bei allen mit der glitze gleichen Waffe rumgelaufen und hast immer genau die selbe Abläufe immer und immer wieder gemacht . Hier musst du Ãœberlegen wie positioniere ich meinen Sturmsoldaten damit er überhaupt schaden macht. Und der Berseker ist im offenen Gelände einfach nur ein Witz ... Denn er läuft mit jedem treffer immer näher zu dir, mit anderen Worten knalle mit dem Scharfschützen drauf, dann mit ner anderen Einheiten und dann mit dem Sturmtypen und er liegt Tod im Dreck ..... Und warte ab bist du auf den ersten Sektopoden triffst das vieh überlebt 3-4 Treffer bis es in die Knie geht. Du kannst ja mal im Multiplayer gegen mich zocken dann zeig ich dir wo der Hammer hängt ;D Zu deinem letzten Satz du pulverisierst nicht die Waffen sonder sie zerstören sich von selber sagt die Tussi doch ganz klar taube Nuss. Wenn die Waffen liegen bleiben würden wäre das Spiel viel zu einfach dann würde es wie bei Terror from the Deep laufen, sofort Sonarkanone erforschen und alles andere liegen lassen weil du findest eh genug
  FREUND NR.2
Es macht eben genau keinen Sinn wo du deine Soldaten aufstellst. Angenommen da sind Mutans hinter nem Kontainer dann stellst du deine Scharfschützen etwas weiter um die Ecke und deine Sturmtruppen ganz vorne hin. Am besten von beiden Seiten. Das wäre doch ne top Idee? Aber von wegen. Den das ist es doch worüber ich mich so aufregte:
Sobald du die Gegner siehst haben die erstmal Initiative und obwohl du noch von jedem Soldaten volle Aktionspunkte hast laufen die dann einfach irgendwo hin. Deine Aufstellung ist fürn Arsch. Deswegen haben die den Sturmtruppen auch den Skill gegeben mit dem man Laufen und Schießen kann. Da ist es dann auch egal wo die sich befinden, da du sowieso über die halbe Map rennen kannst und dem Gegner dann einen aus nächster Nähe reinpfeffern kannst.
Die zweite Möglichkeit die dir bleibt ist es einen Soldaten nach vorne zu bewegen und allen anderen immer Reflexschuß zu geben. Da dauert ne Mission zwar ewig, aber du hast nicht das Problem, dass sobald dein Gegner dich sieht er entweder total von der Map verschwindet oder mitten in deine Formation läuft ohne das du auch nur ne Chance hast was zu machen.
  ICH
achja ... wo ist das inventar? ich habe 2 unterstützer mit 1 medipak (3 in einem – durch das upgrade). aber der unterstützer ist verreckt also wollte ich zu ihm und sein medikit aufheben ... aber nichts da ... tststs. das mit der panik macht mir iwie keinen sinn. ich habe 99% der karte leergefegt ohne einen kratzer abzubekommen. dann wird eine einzige unit von mir mal bewusstlosgeschossen und schon drehen 3 units durch ... wtf? die sind alle max level. wie gesagt, die hätten das nicht xcom eu nennen sollen, sondern: mass effect tactics. ps, muss man das spiel einmal durch haben, damit man asien als basis wählen kann?
ich glaube ich loose ab, weil mir dauernd geld fehlt, um satelliten zu bauen ... habe schon 4 nationen verloren. so ein dreck.
  FREUND NR.1
Jo das inventar fehlt, ist aber auch das einzige was ich wirklich vermisst. Und nimm einfach keine Typen mit niedrigen Werte in wille und es macht nen riesen Unterschied wo du deine Typen positionierst und in apokalypse war z.B. das Gegnerdesign völlig fürn arsch. Die typen haben sich nur in hitpoints unterschieden.
  ICH
ja aber in dem neuen teil ist es ja nicht anders. das finde ich auch voll lame bei den ganzen games, dass das aussehen iwie keinen einfluss auf die einheit hat. bei den fellwesen wäre es cooler gewesen, wenn ihr fell feuer fangen könnte etc. aber auch das wäre ja nichts besonderes. fortbewegungsart? was kann man da so machen? die bewegen sich ja pro kästchen. hatte wieder fun mit dem berserker. finde der nimmt das strategische total raus. ich positioniere meine units hammergeil (neben benzintanks, autos ^^), mit dem kletterhaken des snipers jumpe ich von dach zu dach (man kann ja ewig weit laufen auf die art ... der hat die ganzen geiseln gerettet). die ganzen snobgegner tauchen auf und es gibt einen nice kampf ... dann kommt aber der berserker und läuft in meine linien rein ... was willst da machen? ich muss halt mit jeder unit auf ihn ballern und so meine runden vergeuden. danach kommt ein zweiter und ein dritter ... und währenddessen ballern die anderen wild herum. ich habe schon übelst kranke aktionen gesehen, wo mein schütze durch die wand geschossen hat. da stimmt was nicht mit der sichtlinie ... patchprob? weil es diesmal übelst falsch war. meiner (grün) hat durch die wand geschossen und den roten getroffen. dabei war nichteinmal ansatzweise eine sichtlinie da. die graue linie zeigt den weg zum roten. was mich noch übelst irritiert ist die prozentanzeige bei der treffsicherheit. ich habe schon sooo oft bei 85%+ gemissed, das kann unmöglich sein. ist bestimmt wie bei fallout tactics. das war dort ein bug ... er hat zwar richtig gerechnet, aber die angezeigte treffsicherheit war falsch gemacht worden, sodass er dir z.b. 45% angezeigt hat, aber rechnerisch am ende nur auf 30% gekommen ist ... dann hast du geschossen und dann nicht schlecht gestaunt, wenn es doch nichts wurde. ich finde es auch komisch .... die tussi sagt, dass die panzerung der alienraumschiffe so hardcore ist und kaum durchdrungen werden kann. in den einsätzen aber kann ich mit normalen waffen die außenwände der raumschiffe abballern ... bitte konsequent bleiben. warum hat der roboter, den man baut, so eine miese treffsicherheit? immerhin ist in ihm die neuste technik. sowas mag ich nicht, wenn man versucht durch erzwungene niedrige werte den spieler in bewegung zu halten. ich weiß noch bei fallout tactics ... wenn die gegnerischen sniper einen so angepisst haben ... bis man sich an die herangearbeitet hatte. was mir noch weiter aufgefallen ist: bei der deckung ist es doch eigenartig, dass man nur in deckung ist, wenn man direkt neben dem objekt ist ... aber theoretisch kann ich auch 100m hinter dem objekt sein und es versperrt ja weiterhin die sicht. bsp: wieso zählt das nicht als deckung? schießt der feind um die ecke?
oder ist es soooo gemeint: just curve it VIDEO
  FREUND NR.2
Jo, das ganze Game ist crap, macht aber trotzdem irgendwie bock. Das komische ist, dass rundenbasierte Taktik ja eigentlich das einfachste Genre zum programmieren ist.
  ICH
das beste bei sowas ist halt das erforschen und der aufbau. ich fand das bei solchen rundenbasierten spielen immer interessant die karte zu erkunden (civ5 etc.) oder der basisbau. bei JA2 fand ich die taktischen einsätze iwann einfach nur zu nervig. ich hatte mehrere einsätze am tag und jeder hat locker 1 Stunde gedauert ... bis ich endlich etwas fertiggebaut hatte ... omg. vor allem die runden waren iwann unerträglich lange geworden. der feind hat so 2m gebraucht, die npcs 2m und dann ich mit meinen 4 einheiten so 30s. ja ... JA1&2 würde ich taktikspiel nennen ... xcom eu ist keins. habe jetzt gegen den berserker was witziges gefunden. 2units links und 2 units rechts ... berserker mittendrin ... ich habe abwechselnd von jeder seite geschossen und der berserker ist die ganze zeit hin und her gerannt ... das ist keine ki ... einfachstes scriptverhalten. habe ihn ruckzuck fertiggehabt. wieso kostet das nachladen der waffe die volle runde? wie mich das ankotzt. der gegner ist genau vor dir und du hast keine munition ... man könnte jetzt denken: nachladen, schießen ... aber nichts da ... ich muss immer weglaufen ... und er läuft immer hinterher. habe jetzt auch diese sektopob-roboter und die mutons mit rüstung. naja ... sind eigentlich kein problem. der roboter suckt einfach nur. da ist ja echt 0 taktik. der roboter ignoriert deckung, kann weit laufen ... sein schuss ist ein dreifachschuss, der dmg im radius > 1 macht ... und dann macht er nach einem zug auch noch reflexschuss ... ja wow. was will man da machen? ach ja ... sich einfach neben ihn stellen. wenn man schon so powerroboter reinmacht, dann würde ich die auch langsamer machen. finde die rettungsmissionen bis jetzt am besten. alles andere ist nur zeitverschwendung. ach ja ... habe heute mal die sonde ins schwarze geworfen und siehe da ... da stehen einige gegner auf dem selben punkt und tun nichts. wollte dann mit schredderrakete reinhauen, aber die reichweite ist leider begrentzt. warum eigentlich? finde das auch etwas lame ... die sichtweite der soldaten ist recht kurz. hatte auf einem offenen feld nen gegner und eine einheit hat ihn gesehen. die andere war 1 kästchen zu weit weg. bsp (prinzip):
mein fazit:
Die grafik ist ganz gut, die effekte sind nice. feuer sieht wie feuer aus und die lichteffekte sorgen für die richtige atmo (habe ich ja immer shcon gesagt, licht und offtopic shiet wie regen). auch der regeneffekt in manchen levels ist sehr nice. komischerweise musste ich nur iwo statt hoch auf mittel stellen, da das spiel sonst permanent nach 1m abgerotzt ist. die landschaftslevels finde ich jedoch saulame. zu wenig dickicht. das gameplay ist durchschnittlich ... an einigen stellen ist es einfach iwie nicht stimmig. z.B.: man geht in die kaserne und will die soldaten ausrüsten. man klickt auf bewaffnen und sieht ja dann die einzelnen ausrüstungsgegenstände. mit zwei tasten kann man links und rechts die nächste einheit wählen. ABER: wenn es einen einsatz gibt und man dann die einheiten bewaffnen will, so kann man nicht mehr links/rechts blättern. wenn man also gerade ein falsches team drin hat ... dann muss man die alle raustun (davor umzurüsten nicht zu vergessen!) und dann die neuen rein und ausrüsten. allgemein, wenn man in die kaserne reingeht sieht man die namen und die nationalitäten ... (wäre mal cool, wenn bei der flagge ein tooltip angezeigt werden würde, welches land das denn ist.) ... aber nicht die ausrüstung. minikarteikarten wären hier wohl besser. warum kann man keine festen teams erstellen, wo man bei einsatzbeginn immer aussuchen kann team A oder team B und die werden dann entsprechend gleich umgerüstet?
diese laufen/schießen oder laufen/laufen geschichte ist nett. vielleicht sogar besser als das klassische mit den Zeiteinheiten ... aber auch nicht wirklich neu. JA2 hatte zwar zeiteinheiten ... aber bereits fallout 1 hatte sowas nicht. bei fallout 1 hatte man auch so 5~8 punkte und man konnte mit einer bestimmten punktzahl was anstellen. dabei war es in fallout aber egal, ob man läuft und dann schießt oder umgekehrt. die bewegungsreichweite ist teils zu weit ... man kann ja mit diesem sturm ja schon fast durch die ganze karte rennen und den feind, trotz 99% treffsicherheit, verfehlen.
was soll eigentlich der scheiß, dass wenn 4 einheiten feldposten haben und ein gegner läuft rein ... dann ballern alle 4 gleichzeitig auf den gegner?! danach kommt ein zweiter und für den hat natürlich keiner mehr genug aktionspunkte. so ein stuss. warum muss die deckung explizit angegeben werden, in form von blöden symbolen? ist der spieler zu dumm, um zu wissen, dass wenn zwischen ihm und dem gegner keine sichtlinie besteht, dass er dann in deckung ist? die einheiten können durch wände schießen, granaten hindurchwerfen etc. etc. ... da könnte ich sowas von ausflippen. basisbau ist schon gut ... geht halt in die tiefe, statt in die breite. so gesehen auch nichts neues, aber das aussehen war schon top. ein wenig mehr personen in den räumen würde aber lebhafter wirken. aber was soll der müll, dass es nur eine basis gibt? das ist ja sowas von nicht xcom-style. an sich wäre es vernachlässigbar (da man ja sonst bei dem einheitenmenü eh niemals den durchblick bewahren könnte), aber man muss ja dauernd seine scheiß flugzeuge in aller welt stationieren, da hätte man gleich eine base errichten können lassen. und wenn eine nation austritt, dann verliert man die base ... das würde für etwas druck sorgen. die technologiegeschichte finde ich zu lame ... nicht nur, dass es einen superscheiße zwingt gewisse dinge zu erforschen, der technologiebaum ist ja sowas von lame. autopsien ... dann waffe+1, waffe+2, waffe+3 ... rüstung+1, rüstung+2 etc. etc. Das ist lame³. einige der techs, da frage ich mich, wofür ich die eigentlich brauche? das shiv teil kann man sich ja voll stecken. kampfstimulanzen ebenfalls. so einiges eigentlich ... ich spiele das ganze spiel mit maybe 3 haupttechnologien durch. die pistole ist ja mal der größte scheiß ever ... ich habe die nur 2 mal eingesetzt ... aber der dmg ist nahezu 0, die treffsicherheit ist ebenfalls nahezu 0. wofür hat man die überhaupt? die hauptwaffen haben eh alle unendlich viel munition. allgemein diese ganze munitionsgeschichte kotzt mich an ... ich finde es lame und unreal, wenn waffen keinerlei munition brauchen. in den anderen spielen musste man wie behindert durch die karte latschen und neue munition aufsammeln ... falls man mal wieder knapp dran war. dass das inventar entfernt wurde find ich ebenfalls scheiße. 2 unterstützer mit insgesamt 6 medikits ... => jedes mitglied kann 1 geheilt werden ... tja ... wenn aber der unterstützer fällt, dann war’s das mit heilen. man kann ja nicht hingehen und sein zeug aufsammeln und weiterbenutzen. sehr lame. die gegnerart orientiert sich am spielfortschritt ... was dazu führt, dass man ab den mutons nahezu nie wieder die sektoiden sieht. so konnte ich bestimmte anfragen von anderen nationen nicht erfüllen, weil ich keine leichen mehr hatte ... und zwar gar keine und das schon seit fast 50% des spiels. nur noch mutons und diese drecksroboter ... eventuell die telephaten ... das wirkt öde und nervt irgendwann. auch die “dünnen männer sind kaum noch anzutreffen. dumm ist auch, dass man im späteren verlauf des spiels neue rekruten kaum ausbilden kann, da die gegner einfach nur noch zu overpowered sind. die KI ist ja wohl die größte scheiße seit menschengedenken. dass die einfach nichts tun und an ihrem spawnpunkt stehen, bis man die entdeckt ... omg. anschleichen ist nicht möglich, wird der gegner ohne deckung angetroffen, so kommt sofort initiative von den gegnern und die verstecken sich. was für ein dreck. ein paar minen wären hier nützlich gewesen. so war es bei fallout tactics in einer mission: da wurde einfach ein fettest minenfeld ausgelegt, um den spieler beim vorwärtskommen zu behindern ... aber es war richtig gut gemacht.
ich glaube ich muss nichts mehr zu der ki sagen ... habe mich schon davor übelst beschwert. sound ist okay. karten wiederholen sich ... langweilt irgendwann. warum müssen aliens, die in der lage sind übelste tech zu entwickeln, sich immer wie monster verhalten und auch wie monster reden? die geben ja nur brüllgeräusche von sich. ich fand den einen spruch von der wissenschaftlertussi so behindert: wie kann man nur so weit entwickelt sein und sich so böse verhalten? ... ja hallo? wie sieht es denn auf der erde aus ... industrienation“> entwicklungsland??? so ein dummes geschwätz. dass die aliens immer die bösen sein müssen ... lamed. wieso nicht mal sowas: menschen gehen auf den mars. nach 100 jahren entwickeln sie dort städte etc. und leben dort. iwann kommen aliens vom neptun und wollen ihre lager reaktivieren, die unter der marsoberfläche gebaut wurden. dumm nur, dass die städte oben drauf sind ... tja ... die brauchen die lager, weil sie sonst verrecken ... also holen sie sich das mit gewalt.“> krieg.
dann würde ich die aliens auch so machen, dass die reden können und sowas. ich meine, es gab schon so viele remakes von ufo/xcom ... und immer wieder die selbe leier ... böse böse dumme aliens. konnten die je in einem der teile reden?
ich würde nicht mehr als 10€ für das spiel ausgeben.
mit xcom/ufo hat es nichts zu tun ... habe mir zum trotz die originale gekauft. FREUND NR.2
Dass die immer auf den losrennen der zuletzt auf sie geschossen hat ist Absicht. Kennste das nicht von Filmen? Da kommt so ein dummer Riese und will gerade auf einen draufhauen. Dann nimmt ein anderer ein kleinen Stein und schmeißt den Stein dem Riesen an den Kopf. Der Riese unterbricht seine Bewegung und verfolgt dann den Steinewerfer.
Ich hab die Pistole oft eingesetzt. Gerade wenn ein Scharfschütze neber einem Gegner steht, oder wenn ich nachladen muss aber der Gegner nur noch 3-4 LP hat. Du bist nur enttäuscht weil dein Lieblingsspiel so kacke geworden ist. Ich hoffe schon lange auf ein Nachfolger von Comandos.
  ICH
stimmt ... die werden immer abgelenkt ... ^^ müsste man mal eine parodie machen, wo der riese auch erst über die schulter zurückschaut ... aber bevor er sich dann endgültig umdreht, bricht er dem anderen das genick. 3~4 dmg mit der pistole? ich mache nur 1~2 ... habe aber auch die laserpistole. ich seh da halt keinen nutzen ... wenn man mit pistole zumindest 2 mal schießen könnte, dann hätte es ein unterschied zum einsatz der hauptwaffe. aber so ... naja. enttäuscht bin ich nicht, ich hatte schon die ahnung, dass es ein flopmake wird. genauso wie bei: ufo afterschock ufo afterlight ufo extraterrestrials ufo aftermath xcom enemy unknown aftermath war das erste remake, dass ich mir gekauft hatte ... dann kam extraterrestrials und seit dem nichts. habe aber alle durchgegamed ... immer gleich lame. einfach nicht wie das original. bei den meisten teilen wurde der basisbau und die forschung übelst ruiniert. commandos war nice. der erste teil zumindest ... glaub der zweite ging auch noch. aber nach allem, was ich über den dritten gesehen habe ... omg.
  FREUND NR.1
Ich finde es ein gelunges remake … bin aber auch nicht so ne pussy wie du und laber immer nur davon alles besser zu machen ;D Wie siehts mit der Seminararbeit aus ?
  ICH
1. Wie siehts mit der Seminararbeit aus ? 2. bin aber auch nicht so ne pussy wie du und laber immer nur davon alles besser zu machen -seems legit. ^^ ... ich habe ja nicht gesagt, was ich besser machen würde, sondern das aufgezählt was in den originalen bereits vorhanden war. die hätten es einfach nur übernehmen müssen. aber haben die nicht, die wollten schnell Geld mit iwas verdienen ... ich meine ... siehe JA3 ... was macht JA aus? genau: rundenbasierte Taktik. was sagt der Entwickler von JA3? genau: wir haben uns die Vorgänger angeschaut und haben das beste daraus genommen ... und was war am ende? kann man ja mal rumfragen, wer von euch den alles JA3 original hat. ich wette keiner. so viel dazu. ach was, was hat das mit herumpussien zu tun? wenn man eine checklist macht, links xcom TFDT/UFO und rechts xcom eu, dann sieht man schon, dass da gewaltig was an spielmechanik fehlt. 3. Ich finde es ein gelunges remake
-du hast ja auch viel Geld dafür gezahlt. ^^ ... aber sonst, für diesen Satz sollte ich dich dem NSA melden und nach Guantanamo schicken lassen. entweder das oder es sagt einfach nur aus, dass du von XCOM einfach nichts hältst.
  FREUND NR.1
Nur dumm dass halt auch ein paar Sachen dazugekommen sind. Aber egal ich klick mich aus der Diskussion aus und ja mir gefällt das Spiel sehr sehr gut und halte es für einen absolut gelungen Remake. Ich zock jetzt bestimmt schon das 30x mal classic mit Ironman und verrecke immer noch und das gefällt mir an einem spiel . Es ist nicht weichgespült wie die meisten anderen Titel, es fängt die Atmosphäre wunderbar ein und bis auf 1-2 Kleinigkeiten hab ich nichts zu meckern und in TFDT/UFO gab es menge Dinge die mich mehr geärgert haben siehe lenk Raketenwerfer mit dem das Spiel zum Kindergarten geworden ist usw.

Mr. Clown

Mr. Clown

 

Apocalypse v0.2.0

Hallo Community,

Da ich auch nun mal beschlossen habe einen Entwicklerblog zu erstellen, werde ich hier regelmäßig neue Updates & Screenshots Preisgeben. Nun da ich nicht lange Reden möchte, denn wie man weiß Lange Rede kurzer sinn, oder langer sinn und kurze Rede. Aber egal.


[center][attachment=1165:screenshot05alphav0.2.0.bmp][/center]


v0.2.0 Update Namensänderung zu "Apocalypse" und Story geändert. zudem Screenshots hochgeladen.

Story Apocalypse


Wird noch nicht Veröffentlicht!

Bald wird die Closed Beta erscheinen. Ich Bitte um Rückmeldung als PN wer mitmachen möchte.
Danke!

[center][attachment=1164:screenshot04alphav0.2.0.jpg][/center]

Dev

Dev

 

Ich und Unity

Hallo Zusammen,
Jetzt bekomm ich von Arbeit Unity4 vorgesetzt und dann hänge ich da alles neu...

Was mach ich wohl am besten als erstes.... um diese Engine zu begreiffen?
Ich glaub ich schau mir mal die Refernz von Unity selber durch bevor ich mich an etwas ran wage.

Liebe Grüße

Exo

Exo

Exo

 

Tag 2.....Eine Datenbank

hallllooooooo......

So heute ein kleiner erfolg für mich ich hab heute in die tasten gehauen wie eine ganz große...ich hab eine datenbank mit ca 150 einträgen gemacht.... und jetzt tun mir die finger weh...

positiv ich muss das später nicht mehr machen und kann mich dann voll auf das programmieren konzentrieren.....aber ne datenbank schreiben ist ja nicht schwer....sowas hab ich schon mal voooorrrrrrr langer langer zeit gemacht....da gab es noch dos...grins und windows 98.....

also nicht das ihr alle denkt ch kann ne datenbank unter dos schreiben.....es geht um den zeitraum....
wenn ich das könnte würde ich nicht mehr hier sitzen sondern nur noch urlaub machen....

und in diesem sinne gute nacht morgen gehts weiter wenn mein kleiner schläft....

viele grüße
mia

Mia2109

Mia2109

 

Hallo erstmal....

Hallo...

Ich möchte mich heute und hier erstmal vorstellen....Ich heiße Maria und bin 26 Jahre....
Möchte programmieren lernen da ich das andauernd bei meinem Freund sehe....und ich habe nen paar Ideen, die ich gerne umsetzen möchte....bitte seid nicht so kleinlich was rechtschreibfehler angeht,das ist nur nen "Tagebuch" und da achtet eh keiner auf Rechtschreibung....

Ich gebe mein bestes jeden Tag was zu schreiben wie der Stand der Dinge ist,aber bei nem Kleinkind ist das manchmal nicht machbar.....Soooooo und jetzt zum wesentlichen....

Also ich möchte eine App programmieren,die für alle Smartphone(egal ob Apple oder Android) spielbar ist...
Ich weiß ich weiß....viele kommen und werden sagen fang mit was kleinem an.....aber wie sagt man no risk no fun....

aber ich bin nicht der Mensch der sich mit kleinigkeiten abgibt....was ich mir in den kopf setze versuche ich umzusetzen und wenn ich mir den kopf daran zerbreche warum das nicht geht....

So heute ist folgendes schon passiert....
ich habe mir schon die arbeit gemacht meine idee zu papier zu bringen wie die app funktionieren soll und wie sie aufgebaut sein soll....

und jetzt werde ich noch ein paar video2brains anschauen....weil fernsehen bildet...lach

so denn bis morgen

eure mia

Mia2109

Mia2109

 

UltraTerrain Dev Diary #6

Hab die Arbeit wieder an UltraTerrain aufgenommen und schon einige Tests der bisherigen Strukturen machen können.

Die ganzen bisherigen Bugs konnte ich beheben (meist Deadlocks oder Raceconditions) und habe dabei viele Subsysteme umgeschrieben:

Fehler beim Speichern der Daten in das VFS: War die Menge die geschrieben wurde zu gering konnte es passieren das ein neuer Speicherblock nicht angefordert wurde.

Threading System: Habe den UTH verwendet und stark aufgemöbelt, der UTH bietet nun eine bessere Grundstruktur (Nur noch Task und Task<T> als Basiselemente. Elemente der TPL wurden übernommen und ein brauchbares Eventsystem wurde implementiert, wodurch ich nun mehrere asynchrone Aufgaben elegant aneinander Ketten kann und auch in alle relevanten Threads kommunizieren kann.

Page und Voxel System wurde stark überarbeitet:
In einem vorherigen Blogpost wurde ja bereits beschrieben wie das Material und Datensystem aussieht, dies hat sich nun aber weiter verändert und nun bin ich einen Schritt zufriedener. Da nun einige Subsysteme des Voxelsystems weiter getrennt voneinander wurden.

Ein VoxelObject (das Objekt was alles verwaltet) wird nun wie folgt erzeugt:
[CODE]
var voxelDataDescriptor = VoxelDataDescriptor.Create().With<int>("TileId");

var voxelObjectDescriptorData = new VoxelObjectDescriptorData()
{
WorldPageBounds = new Box(new Integer3(int.MinValue, int.MinValue, 0), new Integer3(int.MaxValue, int.MaxValue, 1)),
ChunksPerPage = new Integer3(2, 2, 2),
VoxelsPerChunk = new Integer3(5, 5, 10),
ChunkSizeInWorldUnits = new Integer3(10, 10, 10),
PageDataGenerator = new PointPageGenerator(),
ChunkMeshProvider = new TileProvider(),
DataStorage = new DataStorage("VoxelObject.voxel"),
UnusedPageUnloadTimeOutInMilliseconds = 20000,
VoxelDataDescriptor = voxelDataDescriptor
};

voxelObject = new VoxelObject(voxelObjectDescriptorData);
[/CODE]

Dieser Code ist direkt aus meinem Beispielprojekt entnommen, welches nur dazu dient eine 2D Welt (Top Down) darzustellen, die in der Höhe dennoch mehrere Voxelebenen enthalten kann.

Die WorldPageBounds geben an welche Reichweite die erzeugten Pages haben dürfen, hier zb ist die Welt auf eine ziemlich Große 2D Ebene beschränkt ohne Platz für Pages über und unter dem Nullpunkt.

Pro Page können 2³ Chunks erzeugt werden, Chunks enthalten zB Meshdaten, etc.
Jeder Chunk enthält 5x5X10 Voxel (250 Voxel pro Chunk).
Und jeder Chunk hat eine Größe von 10x10x10 Welteinheiten.

Zum generieren der Voxeldaten für jede angeforderte Page wird eine PointPageGenerator Klasse verwendet, in meinem Fall erzeugt diese Klasse einfach nur kleine Hügel, das ist quasi der Weltgenerator.

Der TileProvider wandelt hier wiederum die Daten der Page für einen einzelnen Chunk in die Daten für den Chunk um. Hier würden zB für jeden Voxel ein Tile generiert, zb ein Grasteil für einen Grasvoxel.

Der DataStorage gibt an in welche Datei die persistenten Daten gespeichert werden sollen. Dies passiert aber nur wenn Chunks dies wollen (Kann je nach Chunkmesh Klasse spezifiziert werden) oder wenn eine manuelle Anderung der Voxel eines Pages vorgenommen wurde (irgendetwas wurde abgebaut oder eben verändert).

Nicht benötigte Pages und Chunks werden nach 2 Sekunden entladen. Unbenötigt sind sie dann wenn niemand für den Bereich in dem die Page liegt eine Anforderung gestellt hat. Meist wäre dies der Fall wenn der Bereich zu weit vom Spieler entfernt liegt.

Als letztes gibt es den VoxelDataDescriptor, der angibt was ein Voxel an Daten enthalten kann. In diesem Beispiel möchte ich zB nur die TileIds in den Voxeln speichern und zwar als int (byte oder ushort würde hier komplett ausreichen um Speicher zu sparen.)

Ohne weitere Parameter hat diese TileID die Wirkung dass es das ganze ChunkMesh zur kompletten Regenerierung zwingt. Es gibt hier als Parameter 3 Optionen:
1. Gar nichts am ChunkMesh ändern
2. ChunkMesh soll nur geupdated werden
3. ChunkMesh muss komplett neu aufgebaut werden

Das VoxelObjekt liefert nun auch eine Reihe von Events, zB wenn eine page geladen/entladen wird, ein Mesh geupdated oder erstellt oder gelöscht werden muss. Solche Dinge.

Das VoxelObjekt arbeitet im Hintergrund immer, intern gibt es einen TickThread der das VoxelObjekt alle 1000 Millisekunden aktualisiert, verwaiste Pages entfernt (gelöschte Pages werden eine Zeit lang mithilfe von WeakReferences gecached) und auch Pages und Chunks bereinigt die nicht mehr gebraucht werden.

Man benachrichtigt das VoxelObjekt über die benötigten Daten so:

[CODE]
var instantArea = new Box(playerPosition, voxelObject.Descriptor.PageVoxelSize);
var delayedBorder = voxelObject.Descriptor.PageVoxelSize * 1
var type = RequirementType.ChunkMesh | RequirementType.VoxelData;
var requirement = new Requirement(instantArea, delayedBorder, type);

voxelObject.TrackRequirement(requirement);
[/CODE]

Die instantArea gibt den Bereich an der sofort geladen werden muss, dabei wird das VoxelObjekt blockieren bis alle nötigen Daten in diesem angegebenen Bereich (Bereich ist angegeben als Voxel) geladen wurden.

Der delayedBorder gibt an um wieviele Voxel in jede Achse der Box/Area der Lade Bereich ausgehend der instantArea erweitert werden soll. Dieser Bereich wird dann im Hintergrund geladen und kann irgendwann fertig werden. Das anfordern dieses extra Bereiches wird dabei nichts blockieren.

Der type gibt wiederum an was genau wir eigentlich in diesen Bereichen haben wollen. In diesem Beispiel sind es sowohl Voxeldaten als auch ChunkMesh Daten. Wenn wir zB ein Spiel basteln bei dem wir nichts abbauen/verändern könne, ist es zb nicht notwendig die Voxel Daten permanent im Speicher zu behalten. Das laden der Chunk Daten sorgt passiv dafür dass diese VoxelDaten geladen werden sollte es noch kein ChunkMesh geben.

Zu guter letzt noch ein wenig Code um zu zeigen wie man jetzt die VoxelDaten verändern kann:

[CODE]
var box = new Box(playerPosition, new Integer3(10, 10. 10));
var mapRequest = MapRequest.Create()
.ForMaterials(0)
.ForBox(box)
.ForBuffers("TileId")
.ReadAndWriteAccess();
using (var mapHandle = voxelObject.Map(mapRequest))
{
var tileIds = mapHandle.GetBuffer<int>("TileId", 0);
for (int i = 0; i < tileIds.Length; ++i)
{
if (tileIds[i] != 0)
tileIds[i] = 1; // Grass
}
}
[/CODE]

In diesem Beispiel sagen wir dem VoxelObject dass wir gerne einen 10x10x10 Bereich um die Position des Spielers verändern wollen. Wir fordern das Material mit dem Index 0 an und interessieren uns nur für die TileIds und wollen dabei auch die aktuellen Daten haben.

Die aktuelln Daten benutzen wir um in diesem Beispiel nur Tiles zu verändern die eine TileID ungleich 0 haben, 0 steht dabei für "hier ist nichts". Sprich wir machen alles was existiert in einem 10x10x10 Bereich um den Spieler zu Gras.

Das war es soweit, letztendlich noch ein Screenshot wie es aktuell aussieht, nicht spektakulär aber zum testen und debuggen reicht es mir bisher aus:

[attachment=1808:Untitled2.png]

[attachment=1809:Untitled3.png]

Mark

Mark

 

UltraTerrain Dev Diary #5

Ich hatte bisher immer das Problem mich zu entscheiden was für Daten ein einzelner Voxel denn speichern sollte, bisher war das ganze System darauf ausgelegt den VoxelWert sowie 4 MaterialWerte zu speichern:

[CODE]
float[] voxelValues;
Vector4[] blendValues;
[/CODE]

Das ganze ist natürlich äußerst unflexibel, zB was wenn man mehr als nur 1 Material (mit max 5 Texturen) haben möchte? Oder zusätzliche Informationen speichern möchte wie zB die ungefähre Umgebungshelligkeit oder Statikinfos. Dies zu erweitern hätte an vielen Stellen immer wieder dazu geführt dass der Code angepasst hätte werden müssen.

Dass dies so nicht bleiben konnte war klar, von daher habe ich nun ein neues System gebastelt welches auf Reflections verzichtet dennoch alles nötige an Informationen für mich liefert.

Benutzt wird es indem man zuerst beim VoxelDescriptor das Format spezifiziert:

[CODE]

descriptor.VoxelDataDescriptor = new VoxelDataDescriptor(
VoxelDataDescriptionType.Float4 | VoxelDataDescriptionType.Mesh,
VoxelDataDescriptionType.Float4 | VoxelDataDescriptionType.Mesh,
VoxelDataDescriptionType.Float1);
[/CODE]

In diesem Beispiel wird ein VoxelDataDescriptor erstellt, der immer intern ein Float für die VoxelWerte bereitstellt und zusätzlich per Konstruktor 3 weitere Werte bekommt.

Die ersten beiden Werte sind jeweils 4 Floats (Vector4) welche dem Mesh dienlich sind.
zB könnten das 2 Blendwerte für bis zu 9 Texturen sein, der Flag für das Mesh dient dazu dass intern entschieden werden kann was denn bei einer Veränderungsaktion alles am Mesh aktualisiert werden muss. Werte mit dem Mesh Flag sorgen dafür dass sie dem Meshgenerator zugetragen werden müssen. Werte ohne dieses Flag sind für andere Zwecke bestimmt (der Benutzer kann entscheiden was er damit macht).

Der letzte Wert ist nur ein einfacher Float Wert, hier kann man sich zB dazu entscheiden anzugeben wieviel Lebenspunkte der Voxel hat bevor er zerstört wird (VoxelWert auf 0 setzen). Da er dem Mesh nicht viel bringt bei der Generierung/Update hat dieser Wert auch den Mesh Flag nicht.

Nachdem nun dieser VoxelDataDescriptor gesetzt wurde werden alle Pages damit arbeiten und entsprechend Speicher reservieren, speichern, laden und komprimieren können.

Möchte der Benutzer nun Daten ändern kann er diese einfach anfragen. Die VoxelDataDescriptionType Informationen werden nur intern verwendet, nach außen hin funktioniert die Abfrage etwas anders:

[CODE]
var mappedData = voxelObject.Map(box, MapType.ReadWrite,
VoxelDataType.VoxelValues,
VoxelDataType.Mesh0,
VoxelDataType.Meshl1,
VoxelDataType.Data0);
[/CODE]

Hiermit werden alle betroffenen Pages (Pages die von der box abgedeckt werden) geladen und alle Daten angefordert die der Benutzer haben möchte. In diesem Beispiel sind das Die VoxelDaten, sowie die beiden anderen für die Meshgenerierung benötigten Werte und der nicht für die Generierung benötigte Wert.

Um nun zB die 2ten "BlendValues" zu bekommen geht das recht einfach so:

[CODE]
Vector4[] blendValues = mappedData.GetData<Vector4>(VoxelDataType.Mesh1);
[/CODE]

Als Abschluss noch ein Beispiel wie man um einen bestimmten Punkt herum eine Kugel erstellt:

[CODE]

var spherePosition = new Integer3(10, 10, 10);
var sphereRadius = 8;
var sphereBox = new Box(spherePosition - new Integer3(sphereRadius/2, sphereRadius/2, sphereRadius/2),
new Integer3(sphereRadius, sphereRadius, sphereRadius));

using (var mappedData = voxelObject.Map(sphereBox, MapType.ReadWrite, VoxelDataType.VoxelValues))
{
var voxelValues = mappedData.GetData<float>(VoxelDataType.VoxelValues);
for (var i = 0; i < voxelValues.Length; ++i)
{
var position = DimensionUtility.Convert1DTo3D(i, sphereBox);
if ((position - spherePosition).Magnitude <= sphereRadius)
voxelValues[i] = 1.0f;
}
}
[/CODE]

Kleine Erklärung zu der Map Funktion. Die Map Funktion kann mit unterschiedlichen Map Typen benutzt werden, es gibt 4 Verschiedene und jeder Typ hat eine eigene Bedeutung:

MapType.Write: Liest keinerlei Informationen aus den Pages aus und alle Arrays die im Rückgabewert enthalten sind haben einfach nur die Standardwerte gesetzt, wenn man zB nur Daten setzen möchte ohne Rücksicht darauf zu nehmen was zuvor gesetzt war, ist dies die schnellste Möglichkeit, beim Unmap/Dispose (using) werden alle Daten aber an die Page weitergeleitet und die alten Daten ersetzt.

MapType.Read: Liest die Informationen aus den Pages aus, schreibt aber beim Unmap/Dispose keine in die Page hinein, das bedeutet auch dass keine Updates bei den Chunks gemacht werden müssen.

MapType.ReadWrite: Liest zuerst die Daten damit der Benutzer sie verwenden kann und speichert die veränderten Daten beim Unmap/Dispose. Ideal um aufeinander aufbauende Veränderungen zu machen. Dies wird auch sicher am häufigsten benutzt.

Map wird die betroffenen Pages auch je nach Typ vor anderen Zugriffen aus anderen Threads locken, so dass sichergestellt ist dass man solange Unmap/Dispose nicht aufgerufen wird sich die Daten auch nicht verändert haben und man so Probleme bekommt.

über Kommentare würde ich mich freuen.

Mark

Mark

 

UltraTerrain Dev Diary #4

Heute schreibe ich mal ein wenig über das Streaming System vom VoxelObjekt und wie alles abläuft.

Als erstes sei gesagt dass das aktuelle System eine Descriptor Klasse für das VoxelObjekt besitzt in der unter anderem geregelt ist nach welcher Zeitspanne Daten entladen werden die nicht mehr gebraucht werden und die Anzahl an umliegenden Elementen die bei bedarf mitgeladen werden sollen.

Diese beiden Informationen werden vom Streaming System rege benutzt.

Zuerst hat man ein VoxelObjekt ohne geladene Pages, da bisher noch niemand Interesse an den Daten bekundet hat. Erst durch Interesse an den Daten wird die ganze Maschinerie in gang gesetzt, bis dahin ist alles was das VoxelObjekt repräsentiert komplette Leere:
[CODE]







[/CODE]

Unser Spieler, der sowohl betrachten als auch editieren können soll wird nun eine Nachricht an das VoxelObjekt senden, diese Nachricht enthält sowohl die Information Dinge betrachten zu wollen (Meshdaten interessieren ihn) als auch Dinge editieren zu können (VoxelDaten interessieren ihn).

Der Ort des Spielers ist mit einem P wie Player markiert:

[CODE]



P



[/CODE]

Das VoxelObjekt wird nun alle Pages im Umkreis zum Spieler inklusive der Chunks laden. Je nach Entfernung mit einer anderen Priorität, wobei eine niedrigere Nummer die höchste priorität markiert, mit der 0 welche andeutet dass die Daten sofort geladen werden sollen (und somit den Hauptthread blockieren solange das laden statt findet):

Die Page auf die sich der Spieler befindet hat die höchste Priorität:
[CODE]



0



[/CODE]

Das VoxelObjekt behandelt direkt angrenzende Pages mit der gleichen Priorität wie die direkt betroffene Page:
[CODE]


000
000
000


[/CODE]

Weiter entfernte Pages (wir wollen in diesem Beispiel 2 Einheiten weiter die Pages laden) haben eine höhere Priorität, je nach Distanz zum "Kernbereich":
[CODE]
2222222
2111112
2100012
2100012
2100012
2111112
2222222
[/CODE]

Bereiche die mit einer 0 markiert sind werden wie gesagt sofort geladen wenn das System die Interessebekundung bekommt, dazu werden die Page Instanzen erstellt wenn nötig.
Da wir uns für die VoxelDaten interessieren wird zuerst ein Load() aufgerufen.

Load() versucht die Voxeldaten für die betroffene Page zu laden und wenn nicht möglich diese zu generieren (hier besteht die Möglichkeit die Landschaft prozedural erstellen zu lassen).

Da wir uns auch für die Meshdaten interessieren wird an alle Chunks der Page ebenfalls ein Load() gesendet, die Chunks versuchen nun aus der selben Datei wie die VoxelDaten die Meshdaten zu laden,
sollten keine Meshdaten existieren werden die Meshdaten anhand der von der Page geladenen Voxeldaten erzeugt. In dem Fall bei dem der Benutzer keine VoxelDaten angefordert hat wird dies die Chunk Klasse übernehmen.

Der Spieler wird jeden Frame die Interesse aufrecht erhalten, wenn er sich bewegt werden dementsprechend neu benötigte Pages und Chunks geladen. Wenn der Spieler ausserhalb der Reichweite einer bereits geladenen
Page umherwandert werden diese verwaisten Pages irgendwann einen Timeout erfahren, bei dem es 3 Level gibt:

[b]Level 1[/b]: Daten komprimieren, der Spieler könnte der Page wieder nahe kommen, daher wird die Page noch nciht entladen, aber der Zugriff auf die VoxelDaten wird verbaut und die Daten schonmal verkleinert.
[b]Level 2[/b]: Daten speichern, der Spieler hat lang genug keine Interesse an der Page gezeigt womit diese Problemlos gespeichert werden kann, die komprimierten Daten werden dabei in eine Datei gelegt und verschwinden aus dem speicher.
[b]Level 3[/b]: Die Page wird entfernt, selbst die Chunks und deren Meshs spielen keine Rolle mehr für die Welt.

Ein kleiner Zusatz zum erstellen der Meshs durch die Chunks, die Chunks die am Rand der Page liegen haben eine besondere Anforderung an das System.
Denn um die Meshs korrekt zu berechnen ist es nötig nicht nur den Bereich des Chunks selbst zu kennen aus dem die VoxelDaten gelesen werden müssen, sondern es muss ein 1 Voxel großer Randbereich mit berücksichtigt werden, dies hat zur Folge das angrenzende Pages die VoxelDaten ebenfalls geladen haben müssen.

Chunks am Rand werden also zusätzlich die VoxelDaten benachbarter Pages anfordern müssen.

über Kommentare würde ich mich freuen.

Mark

Mark

 

UltraTerrain Dev Diary #3

Die Entwicklung geht gut vorran.

Ich habe die Page Klasse sowie die DataHandler dazu nun fertig implementiert. Die Klasse ist ein Monster was Interface Benutzung angeht

[CODE]

public class Page : IPage, IExtractable, ILockable, IMemoryUsageInformer, IChangeTracker, IPresenceNotifier, IDisposable
{
...
}
[/CODE]

Der Benutzer selbst bekommt nur IPage angeboten, hier mal eine kleine Auflistung was die einzelnen Interfaces machen:

IPage:
[quote]Enthält die zur Page gehörigen Chunks, kennt das VoxelObjekt, den Index im VoxelObjekt und die ausmaße der Page in Voxeleinheiten[/quote]

IExtractable:
[quote]Bietet die Extract und Infuse Methoden an um Datenblöcke zu kopieren oder einzufügen.[/quote]

ILockabla:
[quote]Bietet die Möglichkeit den Zugriff zu locken, was ich fürs Threading benötige.[/quote]

IMemoryUsageInformer:
[quote]Bietet die Möglichkeit an den gebrauchten Speicher in Megabyte, Kilobyte und Byte zu erfahren, für Statistiken.[/quote]

IChangeTracker:
[quote]Weiß über vorgenommene änderungen und die Art der änderung bescheid, zB Voxelwerte wurden verändert oder die Materialdaten.[/quote]

IPresenceNotifier:
[quote]Bietet Methoden an mit denen ich Bereiche von Interesse hervorheben kann, zB kann gesagt werden dass die Kamera sich in einem bestimmten Bereich befinden und die Page würde wenn sie betroffen ist alles nötige tun damit die Kamera alles sehen kann.[/quote]

Am interessantesten für diesen Blogpost ist das letzte erwähnte Interface der [b]IPresenceNotifier[/b].

Dieses Interface wird auch vom VoxelObjekt implementiert und angeboten. Das Voxelobjekt würde dann ausgehend von der tatsächlich per Koordinaten angegebenen Page alle umliegenden Pages ebenfalls benachrichtigen. Je weiter diese Pages sich vom Zentrum entfernen desto weniger dringend ist diese Präsenz Benachrichtigung.

Das Bedeutet, dass nur die Pages mit der höchsten Priorität (direkt das Zentrum+1 Page Umliegend) sofort und blockierend geladen werden. Blockierend bedeutet dass zwar mehrere Threads benutzt werden um die Daten zu laden, der Haupthread aber darauf wartet dass diese Aktionen abgeschlossen wurden.

Weiter entfernt liegende Pages werden nicht blockierend geladen, da diese nicht sofort relevant für das Erlebniss sind.

Diese beiden Reichweiten (Kernbereich+Weitbereich) können im Discriptor vom VoxelObjekt eingestellt werden.

Sollte die letzte Präsenz Benachrichtigung zu lang zurück liegen (die Kamera also zB schon nicht mehr in der Nähe sein), wird je nach Benachrichtigungstyp der zuletzt auftrat, zuerst die Daten komprimiert und in den Speicher gelegt und dann später persistent abgespeichert, ausserdem wird die Page aus dem Speicher entfernt bis sie wieder gebraucht wird.

Diese Zeit ist ebenfalls konfigurierbar über den Descriptor des VoxelObjects.

Das ganze dient dazu ein recht robustes Streaming System zu realisieren welches nur die Daten lädt die auch wirklich wichtig für den Benutzer sind. Bereiche die viel zu weit weg liegen werden nicht in den Speicher geladen und verbrauchen damit keinerlei Platz. Auch ist das System flexibel genug zu entscheiden welcher Typ von Daten gebraucht wird.

Fordert der Benutzer an nur ein Besucher zu sein werden keine Voxeldaten geladen, wenn bereits alle nötigen Geometrien (Meshs) erzeugt wurden. Sollten die MEshs noch nicht erzeugt worden sein so werden die Chunks (Chunks sind Unterelemente der Pages welche die Meshs liefern) die Page auffordern alles nötige nachzuladen damit die Daten generiert werden können.

Fordert der Benutzer an sowohl Besucher als auch Veränderer zu sein so werden gleich die Voxeldaten mitgeladen um auf Aktionen des Benutzers sofort reagieren zu können.

Mark

Mark

 

UltraTerrain Dev Diary #2

Nachdem die Tests nun immer umfangreicher werden wächst auch die Funktionsvielfalt.

Im letzten Post habe ich den PageDataHandler erwähnt, dieser ist nun fast vollständig implementiert, dazu musste aber einiges gemacht werden.

Der PageDataHandler soll wie der Name bereits andeutet in der Lage sein die Daten einer Page zu handhaben/verwalten, darunter fällt wie bereits erwähnt das Laden der Daten.

Das Laden der Daten basiert auf 2 Schritten:

1. Wenn noch keine komprimierten (zip) Daten im Objekt vorhanden sind dann wird das dem System zugehörige VirtuelleFileSystem nach den passenden Page Daten gefragt. Das VirtuelleFilesystem ist das VFS aus SaveIt und kann in einer Datei verschiedene Unterdateien speichern und lesen, das ganze ohne jedesmal die gesamte Datei in den Speicher zu laden. Die Daten wurden vorher komprimiert in das VFS abgelegt.

2. Sind die komprimierten Daten vorhanden werden diese dekomprimiert und in die Datenarrays gelegt die ein PageDataHandler hat. Das sind aktuell nur 2 Arrays, einmal die VoxelValues welche angeben wir einflussreich ein Voxel ist (0.0 bedeutet kein VoxelEinfluss und 1.0 bedeutet vollen Einfluss) und die BlendValues, welche angeben wie die verschiedenen Texturen später miteinander verschmelen werden.

Der 2. Schritt ist gesondert vom ersten aus folgendem Grund: Der PageDataHandler soll wenn eine Page längere Zeit unverändert bleibt die Daten zwischenspeichern können um Speicherplatz zu sparen um später wenn die Page noch viel länger unangetastet blieb diese Daten in das VFS zu speichern. Dies soll später dafür sorgen dass nur die Voxeldaten in Speicher verbleiben die unbedingt nötig sind um entweder schnelle änderungen zu ermöglichen oder wieder schnell parat zu sein um änderungen vorzunehmen.

Intern arbeiten die Load und LowerMemorConsumption Methoden mit Asynchronen Operationen die jeder Zeit abgebrochen werden können um zB wärend eine Page grade die Daten komprimiert diese Operation abzubrechen da ich die Daten spontan doch unkomprimiert benötige.

Das System für diese Asynchronen Operationen basiert auf den Unity Threading Helper.

Bestimmte Aktionen können allerdings nicht abgebrochen werden und sind damit auch synchron und nicht asynchron, zB das löschen aller Daten bei dem es keinen Zwischenschritt gibt der noch abgebrochen werden könnte.

Das System zu laden und Speichern der VoxelDaten bedarf aber noch einen weiteren Zwischenschritt um Versionsupdates auf neuere UltraTerrain Versionen zu ermöglichen, aus diesem Grund wird in den Page Daten später noch die Version des Dateiformates (unabhängig vom VFS Format) abgespeichert werden und basierend auf dieser Version werden dann die geeignetesten Speichermethoden gewählt.

Mark

Mark

 

UltraTerrain Dev Diary #1

So..

Da das aktuelle/alte System zwar funktionierte aber recht schwer zu debuggen und sehr unflexibel war habe ich mich dazu entschieden das ganze System komplett neu aufzubauen. Einige Teile werden weiterhin als Codequelle dienen können damit nicht alles neu gemacht werden muss, aber vieles muss neu gemacht werden.

Daher habe ich mich dazu entschieden mal den Weg des Test driven Developments zu nehmen, kurz TDD.

Dazu habe ich mir nun einen groben Plan gemacht was der Voxelpart des UltraTerrains machen und können soll und mir dazu nur ganz grob schlange Interfaces geschrieben. Danach habe ich diverse TextProjekte aufgesetzt (VisualStudio hat den C# Testprojektetyp) und schreibe mir Tests für jede wichtige Funktion.

Vorteil dadurch ist, ich kann schonmal alles so aufschreiben wie ich es gerne hätte und muss danach die Funktionalität schreiben bis der Test erfolgreich absolviert wird. Dadurch kann ich sicher sein dass Einzelelemente genau das tun was von ihnen gefordert wird ohne einzelne änderungen vorzunehmen die einen anderen Bereich im Projekt negativ beeinflussen.

Bisher habe ich einige Grundlegende Elemente getestet wie meine Box Struktur welche für alle weiteren Objekte sehr wichtig ist, die Box Struktur enthält nur die Position und Größe als Parameter, liefert mir aber wichtige andere Funktionen wie das erstellen eines Schnittbereiches mehrerer Boxen oder Iteratoren um Zeilenweise oder Elementweise durch den potentiellen Inhalt der Box zu iterieren.

Diese Box habe ich gebraucht um mein Voxeldaten Extraktions und Infusionssystem zum laufen zu bekommen. Gebündelt unter dem IExtractable Interface brauche ich dies um Datenbereiche komplett in einen neuen Datenbereich zu kopieren um damit arbeiten zu können:[list]
[*]Daten laden
[*]Daten an den Meshgenerator senden
[*]Daten abkapseln damit Verarbeitungen damit gemacht werden können (Tools)
[/list]
Die 2 Tests die die Extraktion und Infusion testen sehen so aus (nur als Beispiel):

[CODE]

[TestClass]
public class ExtractorTests
{
[TestMethod]
public void Extraction()
{
var outerBox = new Box(new Integer3(10, 10, 10));
var innerBox = new Box(new Integer3(5, 10, 10));
var sourceValues = new float[outerBox.Volume];
for (int i = 0; i < outerBox.Volume; ++i)
if ((i % 2) == 0)
sourceValues[i] = 1.0f;

var extractor = new Extractor<float>(sourceValues);

var destinationValues = new float[outerBox.Volume];
extractor.Extract(outerBox, outerBox, destinationValues);
for (int i = 0; i < outerBox.Volume; ++i)
Assert.AreEqual(destinationValues[i], (i % 2) == 0 ? 1.0f : 0.0f);

destinationValues = new float[outerBox.Volume];
extractor.Extract(outerBox, innerBox, destinationValues);
for (int i = 0; i < outerBox.Volume; ++i)
if (outerBox.GetElementPosition(i).X > 5)
Assert.IsTrue(destinationValues[i] == 0.0f);
else
Assert.AreEqual(destinationValues[i], (i % 2) == 0 ? 1.0f : 0.0f);
}

[TestMethod]
public void Infusion()
{
var outerBox = new Box(new Integer3(10, 10, 10));
var innerBox = new Box(new Integer3(5, 10, 10));
var sourceValues = new float[outerBox.Volume];
for (int i = 0; i < outerBox.Volume; ++i)
if ((i % 2) == 0)
sourceValues[i] = 1.0f;

var destinationValues = new float[outerBox.Volume];
var extractor = new Extractor<float>(destinationValues);
extractor.Infuse(outerBox, outerBox, sourceValues);
for (int i = 0; i < outerBox.Volume; ++i)
Assert.AreEqual(destinationValues[i], (i % 2) == 0 ? 1.0f : 0.0f);

destinationValues = new float[outerBox.Volume];
extractor = new Extractor<float>(destinationValues);
extractor.Infuse(outerBox, innerBox, sourceValues);
for (int i = 0; i < outerBox.Volume; ++i)
if (outerBox.GetElementPosition(i).X > 5)
Assert.IsTrue(destinationValues[i] == 0.0f);
else
Assert.AreEqual(destinationValues[i], (i % 2) == 0 ? 1.0f : 0.0f);
}
}

[/CODE]

Und das daraus erstellte Interface sieht so aus:

[CODE]

public interface IExtractable
{
void Extract<T>(Box box, Box innerBox, VoxelDataType dataType, T[] destinationBuffer);
void Infuse<T>(Box box, Box innerBox, VoxelDataType dataType, T[] sourceBuffer);
}

[/CODE]

Der Extractor ist das Objekt welches dieses Interface implementiert, aber ich behalte den Code dazu mal zurück da der Post sowieso schon recht lang ist.

Der Grund wieso es einmal den box und einmal den innerBox Parameter gibt ist folgender:
box gibt den Bereich an der für den kompletten Ziel/Quellbuffer gedacht ist und innerBox den Bereich der nur ein Unterteil der box ist, dadurch kann ich Problemlos einen ganzen Box Inhalt zusammenkopieren mit mehreren zB Extract aufrufen.

Als Beispiel: Mein VoxelTerrain soll aus mehreren Pages bestehen, jede Page hat eine Box welche ihren gesamten Inhalt beschreibt sowie einen PageDataHandler welcher dafür zuständig ist die Voxeldaten zu managen. Möchte der Benutzer zB genau an der Grenze mehrerer Pages Daten bekommen um sie zB zu verändern (er möchte zB graben) dann würde der box Parameter mit der Gewünschten Box zum graben befüllt werden und bei jeder Betroffenen Page die Schnittmenge der box und der Page Box an den Extracktor gegeben werden.

Damit kann ich schön getrennt vom Rest jede Page ihre Daten verwalten lassen.

Wenn alles klappt wie ich wünsche kommt nun der PageDataHandler dran, welcher in der Lage sein soll Prozedural Daten zu generieren (wenn man es implementiert) oder zu Laden. Die Daten temporär zu komprimieren um Speicher zu sparen, bzw die Daten komplett zu entladen (wenn zB nur das Mesh benötigt wird, brauche ich die Voxeldaten nicht mehr). Das Laden soll dabei komprimiert in ein VirtuellesFileSystem erfolgen, welches entweder nur im Speicher vorliegt (ScriptableObject byte Array) oder als Datei (fürs Echtzeiteditieren mit persistenten Welten).

Mark

Mark

 

Texturieren für Dummies, 1: Texturen erstellen

Hallo Leute,
heute möchte ich eine neue Blogreihe anfangen. Genannt Texturieren für Dummies. Also auch für Leute wie mich ;P

Dabei wichtig:
Ich bin weder ein 2D Artist Profi noch sonst was.
Daher ist das auch alles ein Blog.

Meine Save/Load mit txt Reihe ist derzeit eingestellt da ich kaum Zeit habe mir ein komplettes Save/Load System zu überlegen^^

Aber egal, legen wir los.

Zunächst brauchen wir ein Bildbearbeitungsprogramm.
Zum Beispiel GIMP.

Wir beginnen mit einem x-beliebigen Photo, zb dem einer Wiese ([b]siehe gras.jpg im anhang[/b])

Das öffnen wir in unseren Programm und schneiden es auf eine quadratische Form zu ([b]siehe gras_s1 im anhang[/b])
In Gimp wechselt ihr dazu zum Zuschneiden Werkzeug im Werkzeugkasten.

Danach bearbeiten wir erst mal die Helligkeits und Kontrastwerte, damit unsere Textur nicht überbelichtet wirkt.
([b]siehe dazu gras_s2[/b])
Dazu wählt ihr in Gimp [b]Farben->Helligkeit/Kontrast[/b]

Dann testen wir, ob unser Ausschnitt vielleicht schon Kachelbar ist.
Benutzt dazu [b]Filter->Abbilden->Kacheln[/b] und gebt dann eine neue größe ein. Ich würde zu einer doppelten raten (2048*2048 zB, [b]siehe gras_s3[/b])

Seht euch das Vorschau Bild an.
Seht ihr eine hässliche naht, dann schließt das Bild wieder, ohne es zu speichern. Ansonsten könnt ihrs speichern, fertig!

Bei mir wars aber nicht so.
Bei mir war eigentlich nur an den Rändern eine deutliche Naht zu erkennen.
Deswegen werden wir jetzt die rechte seite nach links spiegeln.

Wählt dazu mit dem Auswahlwerkzeug einen Teil der rechten Seite aus und Kopiert ihn ([b]gras_s4[/b]). [b]Verschiebt[/b] ihn ganz nach rechts, [b]spiegelt[/b] ihn mit dem doppelpfeilwerkzeug im Werkzeugkasten und fügt ihn mit dem kleinen Ankersymbol im Reiter Ebenen in das Bild (oder ihr klickt irgendwo in das bild, dann passiert das automatisch, [b]gras_s5[/b])

Jetzt habt ihr ein kachelbares Bild, das aber nun innendrinnen eine Naht hat!
Nun, bei solchen Grastexturen ist es recht schwierig 100%ige nahtlosigkeit zu erreiche, aber es reicht schon eine optische.

Dazu benutzt ihr das Klonwerkzeug.
klickt auf das Schachfigurähnliche Ding, klickt STRG+links auf einen unteren Teil im Bild und vergrößert den radius auf 10. Dann haltet ihr die linke maustaste unten bei beginn der naht gedrückt und zieht die maus nach oben. Voilà! Fast nahtlos! ([b]gras_s6[/b])

Probeweise könnt ihr das nochmal Kacheln und dort wo ihr noch nicht zufrieden seit nachklonen.
Das Ergebnis könnt ihr in [b]Ergebis.jpg [/b]bewundern.

So, ich hoffe ich konnte euch das Texturieren ein wenig näher bringen.
Denk mal ich hab recht schlecht geschrieben, aber was solls^^

In Teil 2 wollen wir eine Normalmap dazu erstellen oder einen Hügel den wir mit dem gras texturieren.

Bis dahin alles Gute,

Xenes

Xenes

Xenes

 

Save/Load mit txt, 4: Buntes Allerlei

„Besser spät als nie!“ heißt es. Und damit willkommen zu Teil 4 der Blogreihe Speichern mit Textdateien. Packen wirs also gleich an, es gibt viel zu tun.

Zunächst einmal möchte ich unser Script aus der alten Reihe verbessern und dann erweitern. Allerdings werde ich nun nicht mehr in JS scripten, sondern in C#.
Daher unser altes Script in C#:

[CODE]
using UnityEngine;
using System.Collections;
using System.IO;
using System;

public class SaveLoad : MonoBehaviour {
public Transform Player;

void Start(){
//zunächst erstellen wir einen Savegameordner
if(!Directory.Exists(Application.dataPath+"/Savegames")){
Directory.CreateDirectory(Application.dataPath+"/Savegames");
}
}

void WriteFile () {
//wir öffnen einen neuen StreamWriter
StreamWriter sw = new StreamWriter(Application.dataPath +"/Savegames/Savegame.txt");
//drei Zeilenschreiben: position x,y,z
sw.WriteLine(Player.position.x);
sw.WriteLine(Player.position.y);
sw.WriteLine(Player.position.z);
sw.Flush();
sw.Close();
//damit schliessen wir den StreamWriter
if(File.Exists(Application.dataPath +"/Savegames/Savegame.txt")){
print("File written!");
}
else{
print("Error writing file");
}
}
void ReadFile () {
//wir öffnen einen neuen StreamReader
StreamReader sr = new StreamReader(Application.dataPath +"/Savegames/Savegame.txt");
//Liest das ganze file
string Inhalt = sr.ReadToEnd();

//schließt den Reader
sr.Close();

//wir füllen ein Array mit unseren Daten...
string[] Zeilen = Inhalt.Split("\n"[0]);

//...und lesen sie aus!
Vector3 PlayerPos = new Vector3 (Convert.ToSingle(Zeilen[0]),Convert.ToSingle(Zeilen[1]),Convert.ToSingle(Zeilen[2]));
Player.position = PlayerPos;
}

void OnGUI() {
if(GUI.Button(new Rect (0,0,150,50),"Save")){
WriteFile();
}

if(GUI.Button(new Rect (0,50,150,50),"Load")){
ReadFile();
}
}
}[/CODE]
änderungen im Vergleich zum alten für die die weiterhin js benutzen wollen:[list]
[*]Wir haben alle System.IO anfänge entfernt (dank dem namespace können wir getrost drauf verzichten)
[*]Wir benutzen den [url="http://msdn.microsoft.com/de-de/library/system.aspx"]System[/url] - [url="http://msdn.microsoft.com/de-de/library/bb979264.aspx"]namespace[/url] für das explizite Konvertieren von string zu float (Convert.ToSingle)
[/list]
Das wars dann aber im Vergleich.

Bevor alles losgeht, will ich drei variablen einführen. Einmal „Filename“ ,„Filending“ und „Filepath“
Alle sind strings. Mit ihnen werden wir komfortabel den Namen unseres Savegames, die Dateiendung und den Dateipfad verändern können:

[CODE] private string Filepath;
private string Filename = "Savegame";
private string Filending = ".txt";[/CODE]

Den Filepath müssen wir in Start() zuweisen:

[CODE]void Start () {

Filepath = Application.dataPath+"/Savegames";

//zunächst erstellen wir einen Savegameordner
if(!Directory.Exists(Filepath)){
Directory.CreateDirectory(Filepath);
}
}[/CODE]

Dann müssen wir noch alle anderen Pfadinfos anpassen:

[CODE] StreamWriter sw = new StreamWriter(Filepath+Filename+Filending);
[...]
if(File.Exists(Filepath+Filename+Filending)){
[...]
StreamReader sr = new StreamReader(Filepath+Filename+Filending);[/CODE]

So, weiter möchte ich ein kleinen Print nochmal beim Loaden einfügen. Was wenn wir gar kein File haben oder es aus irgendeinen anderen grund nicht geladen werden konnte?

Darum fügen wir an die Kopfzeile der Readfile() funktion folgendes hinzu:

[CODE] if(File.Exists(Filepath+Filename+Filending)){
//...
print ("File read!");
}[/CODE]

Und am Ende dann:

[CODE]else {
print ("File does not exist or error reading file!");
}[/CODE]

So, wer jetzt versucht zu laden, aber kein File hat, dem wird unser print ausgegeben.
Das werden auch brauchen für den teil, der jetzt kommt: Savegames wieder löschen.
Dafür erstellen wir uns zunächst einen neuen Button:

[CODE]if(GUI.Button(new Rect (0,100,150,50),"Delete")){
DeleteFile();
}[/CODE]

Um unser File zu löschen, benutzen wir einfach Die File.Delete Funktion. Allerdings müssen wir zuvor prüfen, ob es überhaupt ein Savegame zum löschen gibt:

[CODE]void DeleteFile () {
if(File.Exists(Filepath+Filename+Filending)){
//wir löschen unser Savegame...
File.Delete(Filepath+Filename+Filending);
print("File deleted!");
}
else {
//gibt es nix zum löschen, mache eine fehlermeldung
print ("File does not exist or error deleting file!");
}
}[/CODE]

Ok, jetzt kommen wir zu einer weiteren nützlichen Option. Man stelle sich ein echtes GameMenü vor: Was wird angezeigt? Naja, zunächst einmal alle verfügbaren Savegames, deren Name, wann sie erstellt worden sind und vielleicht die Größe. Dafür benutzen wir ein tolles Feature aus System.IO:
Die Directoryinfo.
Zuvor machen wir uns einen vierten Button, genannt „Show File Info“:

[CODE] if(GUI.Button(new Rect (0,150,150,50),"Show File Info")){
ShowFileInfo();
}[/CODE]

Jetzt legen wir dazu passende Funktion an.
Auch hier wollen wir zuerst prüfen, ob das File erhältlich ist, dann erstellen wir ein neues Objekt, die DirectoryInfo. Sie ist ein Array bestehend aus Fileinfo – Objekten. Aus ihnen können wir die Informationen auswerten. Dazu benutzen wir eine foreach Schleife:

[CODE]void ShowFileInfo(){
if(File.Exists(Filepath+Filename+Filending)){

print ("File exists!");
//wir legen eine neue DirectoryInfo an...
DirectoryInfo dir = new DirectoryInfo(Filepath);
//...durchsuchen jede Datei...
foreach(FileInfo f in dir.GetFiles("*"+Filending)) {
//und lassen Name, Groesse und andere Infos angeben
print (f.Name);
print (f.FullName);
print (f.Length +" Bytes");
print (f.CreationTime);
print (f.Attributes);
print (f.LastAccessTime);
}
}

else {
print ("File does not exist!");
}
}[/CODE]

Das * ist stellvertretend dafür, dass der nur die Dateiendung zählt. Alle files auslesen wäre *.*

Hier Der ganze Code zum Kopieren:

[CODE] using UnityEngine;
using System.Collections;
using System.IO;
using System;
public class SaveLoad : MonoBehaviour {

public Transform Player;
private string Filepath;
private string Filename = "Savegame";
private string Filending = ".txt";

// Use this for initialization
void Start () {

Filepath = Application.dataPath+"/Savegames";

//zunächst erstellen wir einen Savegameordner
if(!Directory.Exists(Filepath)){
Directory.CreateDirectory(Filepath);
}
}

void WriteFile () {
//wir öffnen einen neuen StreamWriter
StreamWriter sw = new StreamWriter(Filepath+Filename+Filending);

//drei Zeilen schreiben: position x,y,z
sw.WriteLine(Player.position.x);
sw.WriteLine(Player.position.y);
sw.WriteLine(Player.position.z);
sw.Flush();
sw.Close();

//damit schliessen wir den StreamWriter
if(File.Exists(Filepath+Filename+Filending)){
print("File written!");
}
else{
print("Error writing file");
}
}
void ReadFile () {
if(File.Exists(Filepath+Filename+Filending)){
//wir öffnen einen neuen StreamReader
StreamReader sr = new StreamReader(Filepath+Filename+Filending);
//Liest das ganze file
string Inhalt = sr.ReadToEnd();

//schließt den Reader
sr.Close();

//wir füllen ein Array mit unseren Daten...
string[] Zeilen = Inhalt.Split("\n"[0]);

//...und lesen sie aus!
Vector3 PlayerPos = new Vector3 (Convert.ToSingle(Zeilen[0]),Convert.ToSingle(Zeilen[1]),Convert.ToSingle(Zeilen[2]));
Player.position = PlayerPos;
print ("File read!");
}

else {
print ("File does not exist or error reading file!");
}
}

void DeleteFile () {
if(File.Exists(Filepath+Filename+Filending)){
//wir löschen unser Savegame...
File.Delete(Filepath+Filename+Filending);
print("File deleted!");
}
else {
//gibt es nix zum löschen, mache eine fehlermeldung
print ("File does not exist or error deleting file!");
}
}

void ShowFileInfo(){
if(File.Exists(Filepath+Filename+Filending)){

print ("File exists!");
//wir legen eine neue DirectoryInfo an...
DirectoryInfo dir = new DirectoryInfo(Filepath);
//...durchsuchen jede Datei...
foreach(FileInfo f in dir.GetFiles("*"+Filending)) {
//und lassen Name, Groesse und andere Infos angeben
print (f.Name);
print (f.FullName);
print (f.Length +" Bytes");
print (f.CreationTime);
print (f.Attributes);
print (f.LastAccessTime);
}
}

else {
print ("File does not exist!");
}
}

void OnGUI() {
if(GUI.Button(new Rect (0,0,150,50),"Save")){
WriteFile();
}

if(GUI.Button(new Rect (0,50,150,50),"Load")){
ReadFile();
}

if(GUI.Button(new Rect (0,100,150,50),"Delete")){
DeleteFile();
}

if(GUI.Button(new Rect (0,150,150,50),"Show File Info")){
ShowFileInfo();
}
}

}[/CODE]

Ja, das wars soweit auch wieder.
Wer sich jetzt ein bisschen mit GUI auskennt, dem wird es ein leichtes fallen, mithilfe eines Textfeld eigene Savegamenamen zu erstellen und mit dem Slider dann alle in ein Menü zu packen.

Mit fortgeschrittenen Scripting Kenntnissen lassen sich mit System.IO leicht ganze Save/Load Systeme erstellen.

Das ist in Teil 5 geplant... Freut euch darauf^^
Es gibt nämlich viel, viel zu verbessern! Dass habt ihr bestimmt selbst gemerkt!

Witzig: ihr könnt jede Dateiendung wählen, die ihr wollt! Egal ob .xxx oder .oXo oder auch .virus - allerdings lässt sich alles mit dem texteditor bearbeiten

Bei weiteren Fragen und Anregungen an mich wenden.

Xenes

Xenes

 

TreffPunkt Tutorial #1

[size=6]TreffPunkt Tutorial # 1[/size]


[u][size=5][1] Installation[/size][/u]
Ladet euch zunächst das Unity-Package von "TreffPunkt" herunter.
[url="http://dl.dropbox.com/u/77136986/TreffPunkt_1.0.unitypackage"]http://dl.dropbox.co....0.unitypackage[/url]
Startet euer Unity-Projekt, macht einen Rechtsklick im "Project"-Tab
und wählt dann "Import Packages -> Custom Packages..." aus.
Im geöffneten Dialog sucht ihr nun das Package und klickt anschließend
auf "öffnen". ÜberprÜft nun im nächsten Fenster ob auch alles
markiert ist und drÜckt anschließend den "Import"-Button.

Als nächstes muss ein neuer Layer angelegt werden. Dazu wählt
ihr im Pull-Down-MenÜ "Edit -> Project Settings -> Tags" aus.
Im Inspector erscheint jetzt eine Liste mit Layern, wählt ein nicht
benutztes aus und benennt es "TP".
Und das war schon alles, ihr könnt nun TreffPunkt verwenden


[size=5][u][2] Erster Schritt[/u][/size]
Um TreffPunkt ordentlich verwenden zu können braucht man
zunächst das Basisobjekt von TreffPunkt, ein Fenster!
Dazu mÜsst ihr einfach nur folgendes im Pull-Down-MenÜ
auswählen: "TreffPunkt -> New -> Window"
und schon habt ihr ein Fenster. So einfach kann es gehen ^^
(TreffPunkt ist natÜrlich nicht auf 1 Fenster beschränkt, ihr könnt
so viele erstellen wie ihr braucht und als Prefab speichern).
Als nächstes fehlen noch TP-Objekte, wie zum Beispiel Label
oder Button. Unter "TreffPunkt -> Add -> ..." findet ihr eine
Liste mit allen möglichen TP-Objekten. Bevor ihr aber ein
TP-Objekt hinzufÃœgt solltet ihr sicher gehen, dass ihr im
Hierarchie-Fenster ein TP-Objekt ausgewählt/markiert habt.
Denn ein TP-Objekt wird nur einem anderem TP-Objekt als
"Child" hinzugefÃœgt!
Nun kennt ihr die wichtigsten Funktionen zum Erstellen eines
3D-GUI-MenÃœs (rein optisch), zum Skripten mit Hilfe des Interfaces
komme ich einer der nächsten Tutorials.


[size=5][u][3] TP-Objekt-Eigenschaften[/u][/size]
Eigenschaften werden im Inspector bearbeitet.

[u]Label :[/u]
[i]Interface Name[/i] - Zugriffsname fÃœr das Interface
[i]Get From Interface[/i] - Text mit Interface verbinden?
[i]Text[/i] - Text des Labels
[i]Color[/i] - Farbe des Textes

[u]Button :[/u]
[i]Texture Sheet[/i] - 2D-Textur
[i]Color[/i] - Farbe des Buttons
[i]Row/Line[/i] - Reihen/Zeilen der Textur
[i]FPS[/i] - Geschwindigkeit der Animation
[i]Interact With Mouse[/i] - Bewegt sich Objekt wenn Maus darÃœber?
[i]InteractPlus[/i] - Wie bewegt es sich wenn Maus darÃœber
[i]Button Message[/i] - "Zugriffsname" fÃœr Interface


[u]Texture :[/u]
[i]Texture Sheet[/i] - 2D-Textur
[i]Color[/i] - Farbe der Textur
[i]Row/Line[/i] - Reihen/Zeilen der Textur
[i]FPS[/i] - Geschwindigkeit der Animation
[i]Interact With Mouse[/i] - Bewegt sich Objekt wenn Maus darÃœber?
[i]InteractPlus[/i] - Wie bewegt es sich wenn Maus darÃœber
[i]Rotation Speed[/i] - Rotation der Textur

mikomi

mikomi

 

SaveIt Entwicklungsstand #2

Nach langen suchen habe ich nun den Fehler gefunden der mich enorme Zeit und Nerven gekostet hatte. SkinnedMeshRenderer wurden nicht korrekt serialisiert.

[b]Was war das Problem?[/b]

Der SkinnedMeshRenderer hat ein bones Property welches ermöglich ein Array von Transforms zu setzen oder zu bekommen. Serialisiert wurden diese bones, auch deserialisiert, nur nicht so dass der SkinnedMeshRenderer etwas damit anfangen konnte.

Da die Transform Komponente erst erzeugt werden konnte wenn das dazugehörige GameObjekt entstand war das deserialisieren der Array Elemente "deferred", verzögert. Aber das zuweisen des Arrays nicht. So konnte aus folgendem Array:

[CODE]
bones[0] = "Rumpf"
bones[1] = "Kopf"
bones[2] = "Bein links"
bones[3] = "Bein rechts"
...
[/CODE]

Dies hier entstehen:

[CODE]
bones[0] = "Rumpf"
bones[1] = "Kopf"
bones[2] = null
bones[3] = null
...
[/CODE]

Was beim zuweisen des bones Properties dafür sorgte dass die SkinnedMeshRenderer Komponente nur unzureichende Daten hatte, mit dem Effekt dass meine Figur sehr verzerrt aussah.

Die Lösung dafür war recht einfach. Ich musste beim SkinnedMeshRenderer Serializer (eine Klasse die nur dafür zusändig ist SkinnedMeshRenderer Komponenten zu serialisieren) das zuweisen des bones Arrays solange verzögern bis alle Transforms für das Array auch geladen wurden:

[CODE]
for (var i = 0; i < boneCount; ++i)
{
var currentIndex = i;
context.Load<Transform>(bone =>
{
loadedBones ++;
myBones[currentIndex] = bone;
if (loadedBones == boneCount)
{
skinnedMeshRenderer.bones = myBones;
}
});
}
[/CODE]

Dies sollte ich später refactoren damit sich niemand selbst darum kümmern muss der eventuell auch vorhat ein Array zu laden welches aus verzögert geladenen Elementen besteht.

Abgesheen vom fixen dieses Fehlers habe ich nun auch folgendes einbauen können:

[b]Eindeutige Identifizierungsnummer:[/b]
Eine einfache GUID als String welcher erzeugt wird wenn ein Objekt noch keine GUID hat und Awake() aufgerufen wurde. Automatismen beim Start einer Szene innerhalb des Editors sorgen dafür dass alle Szenenelemente bereits eine GUID haben und diese nicht immer beim Start neu erzeugt werden (und damit jedes Objekt eine komplett andere GUID bekommt).
Ausserdem sorgt die Mechanik dafür das Prefabs keine GUID bekommen (damit sie beim Instantiate eine komplett neue GUID bekommen können).

Ich benötige die GUID um Objekte eindeutig identifizieren und finden zu können, denn Unitys eingebaute getInstanceID Methode liefert nicht in jeder Session die gleichen Werte und den Namen des GameObjekts kann ich ebenso nicht verwenden da die Namen gleich sein können.

[b]Abspeichern von Valuetype Werten:[/b]
Bisher konnte man direkt an der SaveIt Klasse nur Klassen und Strukturen abspeichern, dies war an den SAveIt internas begründet, jetzt ist es auch möglich alle anderen Typen abzuspeichern.

[b]Benamte Haupteinträge:[/b]
Bisher konnte man nur 1 Haupteintrag in die SaveIt Klasse abspeichern (eine Klasse oder Struktur welche alle anderen Elemente halten konnte). Dies ist nun erweitert worden, es können nun beliebig viele Einträge gemacht werden, solange jeder dieser Einträge einen eigenen eindeutigen Namen bekommt:

[CODE]
var saveIt = new SaveIt();
saveIt.Save(true, "EinBool");
saveIt.Save(3.141f, "PI");
saveIt.Save(myGameObject); // benutzt einen Standardnamen
[/CODE]

Auf Basis dieser änderung ist es nun auch notwendig explizit zu sagen dass die Daten gespeichert werden sollen:

[CODE]
saveIt.Flush();
[/CODE]

[b]Prefabs:[/b]
GameObjekte können nun neben dem laden auch durch prefabs erzeugt werden, dazu hat die Saveable Komponente nun ein Prefab Feld. Der Nutzen des ganzen ist recht einfach: Wenn man ein GameObjekt hat welches Komponenten besitzt die nicht serialisiert werden können, wie es zb mit dem Legacy PartikelSystem der Fall ist, dann kann man ein Prefab benutzen welches diese Komponenten beinhaltet. Dies ermöglich die Hürden von Unity zu umgehen.

SaveIt wird alle weiteren Komponenten erzeugen, auffüllen oder alte Komponenten löschen. Dies gilt auch für Unter-GameObjekte.

Mit all dem gelang es mir nun eine Szene vollständig abzuspeichern und wieder vollständig zu laden, innerhalb des Spiels!

Mark

Mark

 

SaveIt Entwicklungsstand #1 - Neuanfang

Hier mal der aktuelle Stand meines Lade und Speicherassets "SaveIt".

Ich bin nun mittlerweile soweit dass ich problemlos die meisten Klassen, Strukturen und Primitiven abspeichern kann. Was ist damit gemeint? Sowas hier:

Primitiven: int, float, bool, byte, char, string, ...
Klassen: GameObject, Mesh, Material, AudioClip, eben alles was eine Klasse ist
Strukturen: Vector3, vector4, Quaternion, eben alles was eine Struktur ist

Klassen und Strukturen werden dabei gleich behandelt, für jeden Typ wird ein spezialisierter Serialisierer aufgerufen der dafür zu sorgen hat dass die Member entsprechend abgelegt werden. Gibt es keinen spezialisierten Serialisierer wird einfach ein generischer Klassen/Structurenserialisierer aufgerufen der stumpf alle Member (Fields und Properties) durchgeht und versucht diese abzuspeichern.

Da nicht alle Member abgespeichert werden dürfen (da sie zB nur andere Werte unerwünscht verändern würden, was zB bei Properties schnell der Fall ist) gibt es immer eine Liste der verbotenen Member, die beliebig erweitert werden kann.

Das ganze geht schon recht gut von der Hand, hier mal ein Beispielzum laden einer Testklasse:

TestKlasse.cs
[CODE]
public class TestKlasse
{
public TestKlasse andereTestKlasse = null;
public string text = "Hallo du da!";
}
[/CODE]

Speichern:
[CODE]
var testKlasse = new TestKlasse();
testKlasse.andereTestKlasse = new TestKlasse();
testKlasse.andereTestKlasse.andereTestKlasse = testKlasse; // man beachte besonders diese Zeile
testKlasse.text = "ABCDEFG1234";

var saveIt = new SaveIt();
saveIt.Save(testKlasse, "OptionalerName"); // der letzte Parameter ist optional
saveIt.Flush(); // speichert die daten in eine Datei
[/CODE]
Hierbei wird in die Datei "SaveItData.saveIt" gespeichert, man könnte den Dateinamen und die Art der Datei im Konstruktor der SaveIt Klasse angeben.

Laden:
[CODE]
var saveIt = new SaveIt();
var geladeneTestKlasse = saveIt.Load<TestKlasse>("OptionalerName");
[/CODE]
die geladene Klasse sieht nun genauso aus wie die testKlasse die wir abgespeichert haben:

geladeneTestKlasse.text: "ABCDEFG1234"
geladeneTestKlasse.andereTestKlasse.text: "Hallo du da!"
geladeneTestKlasse.andereTestKlasse.andereTestKlasse.text: "ABCDEFG1234"
geladeneTestKlasse.andereTestKlasse.andereTestKlasse: geladeneTestKlasse

SaveIt speichert eine Klasse nur ein einziges mal ab und lädt sie auch nur ein einziges mal, wodurch Speicherplatz gespart wird und mehr, so haben 2 verschiedene Klassen die eine dritte Klasse referenzieren auch wieder nur diese eine dritte Klasse wenn sie wieder geladen werden und nicht 2 verschiedene dritte Klassen die gleich aussehen.

Man kann auch eigene Serialisierer schreiben, dazu gibt es 2 Möglichkeiten, die kompliziertere welche es auch ermöglich die finale Instanz die deserialisiert werden soll zu erstellen benötigt vom Entwickler, dass er von einem BasisSerialisierer ableitet und ein paar Methoden implementiert, hier ist zb ein Beispiel für den ParticleSystemSerializer, welches ein PartikelSystem serialisieren und deserialisieren kann:

[CODE]
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEngine;

namespace SaveItSpace.TypeSerializer.Unity
{
public class ParticleSystemSerializer : ComponentSerializer
{
public override bool IsUsable(Type type)
{
return type == typeof(ParticleSystem);
}

public override void Serialize(object what, SaveContext context)
{
var particleSystem = (ParticleSystem)what;

var particles = new ParticleSystem.Particle[particleSystem.particleCount];
particleSystem.GetParticles(particles);

context.Save(particles, "particles");

base.Serialize(what, context);
}

public override void Deserialize(Type type, LoadContext context)
{
base.Deserialize(type, context);

var particleSystem = (ParticleSystem)context.CurrentInstance;

var particles = context.Load<ParticleSystem.Particle[]>("particles");
particleSystem.SetParticles(particles, particles.Length);
}
}
}
[/CODE]

Die andere Variante die etwas leichter ist, wird implementiert indem man einfach in den zu serialisierenden/deserialisierenden Typen 2 Methoden implementiert:

[CODE]
void Save(SaveContext context, ref bool allowAutoSerialization)
void Load(LoadContext context, ref bool allowAutoDeserialization)
[/CODE]

SaveContext und LoadContext bieten hierbei immer eine Reihe von Methoden zum speichern oder laden von Werten an, dazu kommt aber später noch etwas mehr.

Erstmal ein Beispiel wie diese Methoden verwendet werden könnten:

[CODE]
public class TestKlasse
{
public string text = "Huhu";
public int wert = 123;

void Save(SaveContext context, ref bool allowAutoSerialization)
{
// verhindert Automatismen welche einfach nur jeden Member serialisieren würde
allowAutoSerialization = false;
context.Save(wert); // speichert den Wert ohne besonderen Namen ab
}

void Load(LoadContext context, ref bool allowAutoDeserialization)
{
// verhindert Automatismen welche einfach nur jeden Member deserialisieren würde
allowAutoDeserialization = false;
wert = context.Load<int>(); // Lädt den Wert ohne besonderen Namen
}
}
[/CODE]
Dieses Beispiel würde nur TestKlasse.wert speichern und laden, TestKlasse.text dabei aber ignorieren.

Hier nunmal ein kleiner überblick über die bisherigen Methoden der Save und LoadContext Klassen:

SaveContext:
[CODE]
// speichert den Member mit dem angegebenen memberNamen ab
void SaveMember(string memberName)

// speichert den Member mit dem angegebenen memberNamen unter dem angegebenen namen ab
void SaveMember(string memberName, string name)

// speichert what ab
void Save<T>(T what)

// speichert what unter dem angegebenen namen ab
void Save<T>(T what, string name)
[/CODE]
Lässt man name weg dann wird intern ein Zähler erhöht so dass man durch das entgegengesetzte Load die Werte wieder bekommen kann.

LoadContext:
[CODE]
// gibt true zurück wenn der angegeben Typ nicht direkt erzeugt werden kann, wie es zB bei allen Komponenten der Fall ist
bool IsDeferredType(Type type)

// lädt den angebenen memberName in das aktuell zu befüllende Objekt
void LoadMember(string memberName)

// lädt den angebenen memberName unter dem angebenene Namen name in das aktuell zu befüllende Objekt
void LoadMember(string memberName, string name)

// lädt die Daten ohne Namen in die angegebene Instanz (für zB Komponenten)
T LoadToInstance<T>(T instance)
object LoadToInstance(object instance)

// lädt die Daten unter dem Namen name in die angegebene Instanz (für zB Komponenten)
T LoadToInstance<T>(string name, T instance)
object LoadToInstance(string name, object instance)

// Lädt den Wert im aktuellen Slot und ruft die angegebene Aktion bei Erfolg auf (gut für Komponenten)
void Load(Action<object> onLoaded)
void Load<T>(Action<T> onLoaded

// Lädt den Wert mit dem angegebenen Namen name und ruft die angegebene Aktion bei Erfolg auf (gut für Komponenten)
void Load(string name, Action<object> onLoaded)
void Load<T>(string name, Action<T> onLoaded)

// Lädt den Wert im aktuellen Slot
object Load()
T Load<T>()

// Testet ob es einen Wert unter dem angebenenen Namen name gibt
bool Exists(string name)

// Lädt den Wert unter dem angegebenen Namen name
object Load(string name)
T Load<T>(string name)

// Lädt den aktuellen Slot in das angegebene target
void Load<T>(out T target)

// Wartet darauf dass der aktuelle Slot geladen werden kann und ruft die angebenene Action auf
void WaitForLoad<T>(Action<T> onLoaded)
void WaitForLoad(Action<object> onLoaded)

// Wartet darauf dass der Wert unter dem angebenenen Namen name geladen werden kann und ruft die angebenene Action auf
void WaitForLoad<T>(string name, Action<T> onLoaded)
void WaitForLoad(string name, Action<object> onLoaded)
[/CODE]

Schon eine ganze Menge an Methoden und Möglichkeiten sein Objekt zu individuell zu speichern.

Das sehr schöne an der Sache: Ich arbeite aktuell an einer Klasse welche das gesammte Level laden und speichern kann. Dies klappt bisher auch ganz gut, auch wenn ich noch nicht alle einzelnen Komponenten auf Herz und Nieren testen konnte.

Bisher gibt es nur noch ein Problem mit animierten Figuren, wie zB die Bauarbeiter Figur, irgendetwas wird da noch nicht gut genug geladen, mit dem Resultat dass einige Vertexe der Bauarbeiter Figur beim Ursprungspunkt der Welt (0,0,0) verbleiben. Woran genau das liegtist noch fraglich, am gespeicherten Mesh liegts zumindest nicht, denn:

SaveIt hat die Möglichkeit bestimmte Objekte direkt von Unity zu laden ohne die eigene Serialisierung/Deserialisierung dafür bemühen zu müssen, dies spart enorm viel Zeit und Speicherplatz.

So das wars erstmal mit dem aktuellen Stand der Entwicklung zu SaveIt, ich hoffe ich kann bald mehr zeigen unter anderem einen WebPlayer der in die Playerprefs bzw in den Arbeitsspeicher speichert um alles demonstrieren zu können.

Mark

Mark

 

Save/Load mit txt, 3: Weitere Techniken

Willkommen,
zu meinen dritten Teil der Tutorialreihe zum Speichern und Laden in Unity via Textdatei.
Heute geht es um weitere Techniken, die uns der Namespace System.IO bietet.
Ich werde hier an das Script aus den [url="http://forum.unity-community.de/blog/17/entry-25-saveload-mit-txt-2-laden/"]letzten Teil[/url] anknüpfen.

Zuerst werde ich ein Ordner für unsere Savegames erstellen lassen. Dafür benötigen wir die [url="http://msdn.microsoft.com/de-de/library/system.io.directory.aspx"]System.IO.Directory[/url] - Klasse, mit dem wir auf Ordnerfunktionen zugreifen können, also z.B. neue erstellen!
Dazu öffnen wir unser Script und fügen eine Start - Funktion ein, in der wir folgendes schreiben:

[CODE]function Start(){
if(!System.IO.Directory.Exists(Application.dataPath+"/Savegames")){
System.IO.Directory.CreateDirectory(Application.dataPath+"/Savegames");
}
}[/CODE]

So, jetzt machen wir einen Ordner mit der Bezeichnung savegames, falls er denn noch nicht exisitiert. Theoretisch könnten wir das auch in die WriteFile Funktion packen, aber wir müssen unseren Pfad sowieso nur ein mal erstellen.

Dann müssen wir natürlich auch noch den Speicherpfad im StreamWriter und StreamReader anpassen:
[CODE]var sw : StreamWriter = new StreamWriter(Application.dataPath +"/Savegames/Savegame.txt");[/CODE]
und
[CODE] var sr = new StreamReader(Application.dataPath +"/Savegames/Savegame.txt");[/CODE]

Speichern, ausprobieren!
Siehe da! Es funktioniert. Jetzt haben wir unseren eigenen Savegame Ordner.

Weiter wollen wir die Konsole etwas ausgeben lassen, wenn gespeichert und geladen wurde, da das schreiben einer txt so rasend schnell geht und zumindest ich mich immer frage, ob jetzt eigentlich schon was passiert ist.

Na gut, einfach ein print hinzufügen?
Nein, es geht besser.

Dazu hilft [url="http://msdn.microsoft.com/de-de/library/system.io.file.aspx"]System.IO.File[/url]. Hier können wir auf unser File zugreifen, es löschen, kopieren, etc.
Und auch abfrage, ob es exisitiert. Und das machen wir jetzt.

Also gehen wir in unser Script und schreiben in die WriteFile Funktion hinzu:
[CODE]if(System.IO.File.Exists(Application.dataPath +"/Savegames/Savegame.txt")){
print("File written!");
}

else{
print("Error writing file");
}[/CODE]

Ok, weiter gehts.
Jetzt will ich noch eine weitere Auslesemöglichkeit präsentieren, nämlich alle txtzeilen als (String-)Array ausgeben zu lassen, womit sich auch der Auslesevorgang automatisieren lässt.

Dafür schauen wir uns die ReadFile Funktion an und löschen zunächst alles bis auf die deklaration des StreamReaders. Dann fügen wir folgendes hinzu:

[CODE] function ReadFile () {
//wir öffnen einen neuen StreamReader
var sr = new StreamReader(Application.dataPath +"/Savegames/Savegame.txt");
//Liest das ganze file
var Inhalt = sr.ReadToEnd();
//schließt den Reader
sr.Close();

var Zeilen : String[];
Zeilen = Inhalt.Split("\n"[0]);
}[/CODE]

Wir lesen also zunächst das Ganze File aus und speichern das in ein String. Diesen teilen wir dann Zeile für Zeile auf und fügen dass in ein Array ein. Wer will, kann auch keine lokale varaible definieren und dann im Editor beobachten, wie sich das Array füllt.

Und jetzt müssen folgendes für das eigentliche laden einfügen:

[CODE]//...und lesen sie aus!
Player.position.x = parseFloat(Zeilen[0]);
Player.position.y = parseFloat(Zeilen[1]);
Player.position.z = parseFloat(Zeilen[2]);
[/CODE]

Warum Zeilen[0]? naja, weil ein Array immer mit dem nullten Element anfängt...
So und dass ist der Punkt, in dem man dann automatisieren kann!

Zb mit so was wie:
[CODE]for(GameObject in SaveObjekte){
GameObject.transform.position.x = parseFloat(Lines[a]);
GameObject.transform.position.y = parseFloat(Lines[a+1]);
GameObject.transform.position.z = parseFloat(Lines[a+2]);
a +=3;
}[/CODE]

Für Positionskoordinaten für eine Liste mit SaveObjekten... Die könnte man dann vielleicht auch taggen mit "savable" und am Anfang finden lassen...

So, dass wars jetzt für den dritten Teil. Hier nochmal das Ganze Script:
[CODE] #pragma strict
import System.IO;
var Player: Transform;
function Start(){
//zunächst erstellen wir einen Savegameordner
if(!System.IO.Directory.Exists(Application.dataPath+"/Savegames")){
System.IO.Directory.CreateDirectory(Application.dataPath+"/Savegames");
}
}
function WriteFile () {
//wir öffnen einen neuen StreamWriter
var sw : StreamWriter = new StreamWriter(Application.dataPath +"/Savegames/Savegame.txt");
//drei Zeilenschreiben: position x,y,z
sw.WriteLine(Player.position.x);
sw.WriteLine(Player.position.y);
sw.WriteLine(Player.position.z);

sw.Flush();
sw.Close(); //damit schliessen wir den StreamWriter

if(System.IO.File.Exists(Application.dataPath +"/Savegames/Savegame.txt")){
print("File written!");
}

else{
print("Error writing file");
}
}
function ReadFile () {
//wir öffnen einen neuen StreamReader
var sr = new StreamReader(Application.dataPath +"/Savegames/Savegame.txt");
//Liest das ganze file
var Inhalt = sr.ReadToEnd();
//schließt den Reader
sr.Close();
//wir füllen ein Array mit unseren Daten...
var Zeilen : String[];
Zeilen = Inhalt.Split("\n"[0]);
//...und lesen sie aus!
Player.position.x = parseFloat(Zeilen[0]);
Player.position.y = parseFloat(Zeilen[1]);
Player.position.z = parseFloat(Zeilen[2]);
}
function OnGUI() {
if(GUI.Button(Rect (0,0,150,50),"Save")){
WriteFile();
}
if(GUI.Button(Rect (0,50,150,50),"Load")){
ReadFile();
}
}
[/CODE]

Ich bin noch am überlegen für Teil 4...
Zumindest auf das laden einzelner Szenen werde ich noch, wie von CreativBase gewünscht, eingehen.
Gibt es noch andere Wünsche oder Anregungen? Z.B. eigene Namen für Savegames o.ä.?

Bis dahin weiteren Spass am speichern mit Textdateien,

Xenes

Xenes

Xenes

 

Save/Load mit txt, 2: Laden

Hallo nochmal,
dies ist hier gleich der zweite teil zu meinem Tutorial zum speichern und laden via Textdatei.
Jetzt geht es ums Laden.

Wer das vorige Tutorial mitgemacht hat, der öffnet jetzt seine savegame.txt und hat so was in der Art vorliegen:

[quote]2.54939
1.05
1.030781[/quote]

Jetzt müssen wir das ganze aber noch wieder einlesen und laden.
Wie geht jetzt das?

Dazu stöbern wir bei [url="http://msdn.microsoft.com/de-de/library/29kt2zfk(v=vs.80).aspx"]System.IO[/url] im msdn rum und entdecken folgende Klasse:
den [url="http://msdn.microsoft.com/de-de/library/system.io.streamreader(v=vs.80).aspx"]StreamReader[/url], also dem Gegenstück zum StreamWriter?

Ja, ganz genau.
Also öffnen wir unser Script und gehen zu unserer Funktion ReadFile() und fügen folgendes hinzu:

[CODE]function ReadFile () {
//wir öffnen einen neuen StreamReader
var sr = new StreamReader(Application.dataPath +"/Savegame.txt");
//Lese zeilen aus...
Player.position.x = parseFloat(sr.ReadLine()); //parse Float, weil es als String gelesen wird
Player.position.y = parseFloat(sr.ReadLine());
Player.position.z = parseFloat(sr.ReadLine());

sr.Close(); //damit schließen wir unseren StreamReader
}[/CODE]

Wir brauchen das parseFloat, um die Umwandlung von String zu Float wieder vorzunehmen Alles andere erklärt sich von selbst.

Wie,das wars?
Ja, das wars.

Jetzt nochmal das ganze Script:

[CODE] #pragma strict

import System.IO;
var Player: Transform;

function WriteFile () {
//wir öffnen einen neuen StreamWriter
var sw : StreamWriter = new StreamWriter(Application.dataPath +"/Savegame.txt");
//drei Zeilenschreiben: position x,y,z
sw.WriteLine(Player.position.x);
sw.WriteLine(Player.position.y);
sw.WriteLine(Player.position.z);

sw.Flush();
sw.Close(); //damit schliessen wir den StreamWriter
}

function ReadFile () {
//wir öffnen einen neuen StreamReader
var sr = new StreamReader(Application.dataPath +"/Savegame.txt");
//Lese zeilen aus...
Player.position.x = parseFloat(sr.ReadLine()); //parse Float, weil es als String gelesen wird
Player.position.y = parseFloat(sr.ReadLine());
Player.position.z = parseFloat(sr.ReadLine());

sr.Close(); //damit schließen wir unseren StreamReader
}

function OnGUI() {
if(GUI.Button(Rect (0,0,150,50),"Save")){
WriteFile();
}

if(GUI.Button(Rect (0,50,150,50),"Load")){
ReadFile();
}
}[/CODE]
Es gibt aber noch viel viel coolere Techniken!

Aber dafür gibts dann einen dritten Tutorialteil.

Bis dahin:
Viel Spass beim experimentiern mit txt Dateien!

Xenes

Xenes

Xenes

 

Save/Load mit txt, 1: Speichern

Hallo,
ich beschäftige mich heute mit dem speichern in Unity mit einer Textdatei.
Textdatei? Das ist doch dieser Krampf, der nix kann!
Von wegen!

Zunächst einmal schauen wir uns eine beliebige Textdatei an, oder öffnen eine neue. Wie im Namen schon enthalten, kann eine Textdate nur Text enthalten, also Zahlen, Buchstaben und Zeichen.

Wie jetzt? Unsere drei Grundvariablen sind doch auch nur Buchstaben oder Zeichenketten!
Also Variablen in einer *.txt schreiben? Ja, ganz genau!

[quote]
Was ist mit Vector3, boolean, etc?
Ja, auch die könnten wir schreiben. Aber die einzige Schwierigkeit in einer txt ist das Lesen, nicht das Schreiben. Bei lesen aus einer txt wird nämlich alles aus String ausgegeben. Während wir Zahlen mit dem ParseInt oder ParsFloat wieder zurückkonvertieren können, wirds mit Vektoren oä schwierig.
[/quote]

Aber wie fangen wir jetzt an? Zunächst einmal bauen wir uns eine Szene mit einem FirstPersonController und einer Plane (nicht vergessen: Main Camera löschen)
Dann setzen wir den FPC auf die Plane und erstellen dann ein neues JavaScript, ich nenne es [b]SaveLoad.js[/b]

Dann öffnen wir das Script und haben folgendes:
[CODE]
#pragma strict
function Start () {
}
function Update () {
}
[/CODE]

Wie können wir nun aber ein File schreiben?
Dazu hilft der namespace [b]System.Io[/b]
Der MSDN sagt dazu:
[quote]
Der System.IO-Namespace enthält Typen, die das Lesen und Schreiben für Dateien und Datenstreams zulassen, sowie Typen, die grundlegende Unterstützung für Dateien und Verzeichnisse bieten.
[/quote]

Also nur diesen Namespace importiern und auf die Funktionen zugreifen? Ja, ganz genau!
Also fügen wir unter dem #pragma hinzu:

[CODE] import System.IO;[/CODE]

Danach löschen wir die Funktionen Update und Start und fügen folgende drei hinzu:
[CODE] #pragma strict
import System.IO;
function WriteFile () {
}
function ReadFile () {
}
function OnGUI() {
}[/CODE]

über das GUI wollen wir auf unsere Funktionen zugreifen, also machen wir zwei Buttons:
[CODE] function OnGUI() {
if(GUI.Button(Rect (0,0,150,50),"Save")){
WriteFile();
}
if(GUI.Button(Rect (0,50,150,50),"Load")){
ReadFile();
}
}[/CODE]

Ok, so weit, so einfach.
Jetzt überlegen wir, was wir speichern wollen.
Ich nehme hier jetzt einfach nur die Spielerposition.

Dafür brauchen wir den [url="http://msdn.microsoft.com/de-de/library/system.io.streamwriter.aspx"]StreamWriter[/url]
Mit ihm können wir eine txt mit einem Namen in einen von uns gewählten Pfad speichern.

Also schreiben wir folgendes:
[CODE] #pragma strict
import System.IO;

var Player: Transform;

function WriteFile () {
//wir öffnen einen neuen StreamWriter
var sw : StreamWriter = new StreamWriter(Application.dataPath +"/Savegame.txt");
//drei Zeilenschreiben: position x,y,z
sw.WriteLine(Player.position.x);
sw.WriteLine(Player.position.y);
sw.WriteLine(Player.position.z);

sw.Flush();
sw.Close(); //damit schliessen wir den StreamWriter
}

function ReadFile () {
}

function OnGUI() {
if(GUI.Button(Rect (0,0,150,50),"Save")){
WriteFile();
}
if(GUI.Button(Rect (0,50,150,50),"Load")){
ReadFile();
}
}
[/CODE]

Application Datapath ist der Pfad, wo unsere zukünftige *.exe ist.
Wenn wir das jetzt in unsere Szene hinzufügen und die variable Player assignen, dann auf Save klicken, wird eine neue txt importiert mit unseren drei Positionen.

Das war der erste Teil des Speichern und Ladens via txt.
Es folgt auf jeden Fall noch laden und später vielleicht noch verfeinern mit Ordnern und eigenen Namen des Savegames etc.

Bis dahin:
Hoffe, es war ein erstes gutes Tutorial und hat euch was gebracht!

Xenes

Xenes

Xenes

 

Überlegungen zum Strategiespiel mit Unity, 1: Bauplätze

Hallo,
Ich habe in den letzten Tagen mir Gedanken über ein Strategiespiel mit Unity gemacht. Zunächst habe ich mir die Kamera gescriptet und dann bin ich zu Bauplätzen übergegangen und dort überlege ich, wie ich das am besten angehe.

[quote]
überlegungen:
*Es soll für alle mögliche Anzahl an Bauplätze nur ein Script geben
*Beim Klick soll einer selektiert werden und rot unterlegt
*Wenn dieser selektiert ist, öffnet sich ein Baumenü
*Beim bauen eines Haus verschwindet der Platz und ein Haus erscheint
[/quote]

bisher habe ich mir so was in der Art gedacht:
-Jeder Platz hat den tag "Bauplatz", die Variable Empty (boolean) und Selected (boolean)
-Empty ist standardmäßig true

[CODE]var IsSelected : boolean;
var IsEmpty: boolean = true;
[/CODE]

-Beim Klick (OnMouseDown) wird selected auf true geschaltet...
-...in der Update Funktion wird bei selected == true der renderer rot gemacht
-...die statische visible (boolean) variable des Baumenüs auf true gestellt
-Beim Klick auf einen Button im baumenü wird an eine am Anfang gefüllte Liste voller Bauplätze gesendet...

[CODE]for(Platz in Bauplaetze){
Platz.SendMessage("BuildHouse",1.0,SendMessageOptions.DontRequireReceiver);
}[/CODE]

...Mit der HausID (1 = Bauernhof, 2 = wohnhaus, etc.).
In der Funktion "BuildHouse" wird nun Empty auf false umgeschaltet und ein Bauernhof auf die stelle geklont, aber nur bei dem selektierten Bauplatz!

[CODE]function BuildHouse(ID:int){
//...aber nur wenn der Platz leer und selektiert ist
if(IsSelected && IsEmpty){
switch(ID){
case 1:
//fuer die ID eins, baue haus eins, deselektiere
Instantiate(Haus1,transform.position,Haus1.transform.rotation);
IsSelected = false;
IsEmpty = false;
break;

case 2:
//fuer die ID zwei, baue haus zwei, deselektiere
Instantiate(Haus2,transform.position,Haus2.transform.rotation);
IsSelected = false;
IsEmpty = false;
break;
}
}
}[/CODE]

Jetzt haben wir einen Bauernhof da, der Platz ist nicht mehr leer und automatisch deselektiert.
Jetzt könnte man noch hinzufügen:

[CODE]function Update(){
//wenn er NICHT leer ist, dann blende ihn aus
if(!IsEmpty){
renderer.enabled = false;
}
//ansonsten halt nicht
else{
renderer.enabled = true;
}
}[/CODE]

So dass er auch nicht mehr sichtbar ist.

Problem: Jetzt haben wir aber mehrere Bauplätze und müssten beim Klick auf einen Bauplatz allen anderen mitteilen, dass sie nicht mehr selektiert sind. Sonst könnten wir extrem viele Gebäude gleichzeitig bauen und alles würde selektiert bleiben...

Man könnte natürlich beim Klick allen Bauplätzen sende: ihr seid nicht selektiert!, kurz warten und dann den angeklickten auf selektiert schalten:

[CODE] function OnMouseDown () {
//...wenn das ding leer ist
if(IsEmpty){
//blende BauMenu ein
BauMenu.Visible = true;
//sende an alle Plätze: unselektieren...
var Platz : GameObject;

for(Platz in Bauplaetze){
Platz.SendMessage("SetUnSelected",0.0,SendMessageOptions.DontRequireReceiver);
}
//...im naechsten frame aber teile den angeklickten mit: Du bist selektiert!
yield;
SetSelected();
}
[/CODE]

Die Frage ist doch, welche besseren Lösungen oder Vorschläge es gibt, das zu lösen.
Bin auf Kommentare gespannt...

Xenes

Xenes

Xenes

 

UV-Mapping Tutorial Teil 2/2

Herzlich Willkommen zum 2 Teil meiner Tutorial Reihe. In dieser wird es um die Texturerstellung in Gimp und danach den Import in Blender gehen. Ich verwende die [b]Gimp Version 2.6.[/b]

[b]In Gimp:[/b]

In Gimp müssen wir zuerst das UV Bild öffnen [b][STRG+O].[/b]Danach erstellen wir eine neue Ebene, die wir "Overlay" oder anderes nennen können. Auf dieser Ebene könnt ihr jetzt eure Textur "malen". Es ist wie "Malen nach Zahlen" (Nein ich werde nicht von "Malen nach Zahlen" bezahlt, auch wenn ich den Namen "Malen nach Zahlen" sehr oft verwende ;-) ). Lasst eurer Kreativität freien lauf^^.
Als nächstes erstellen wir eine weitere Ebene und die füllen wir schwarz und schieben die unter die Overlay Ebene. Ach ja, ihr könnt ruhig aus der Form malen ;-).

[attachment=518:gimp.png]

Danach speichert es einfach wieder ab (Am besten als PNG).

[b]Als nächstes wechseln wir wieder zu Blender.[/b]


[b]In Blender:[/b]

Haben wir Blender wieder geöffnet können wir wieder zurück in die Default Ansicht gehen, und mit [b][TAB] [/b]den Edit Mode verlassen. Als nächstes erstellen wir ein neues Material und nennen es "Face". Die Einstellungen können wir so lassen. Als nächstes fügen wir eine Textur hinzu.
Als Type wählt Ihr [b]Image or Movie. [/b]Mit Open öffnet ihr euer zuvor erstellte Textur. Wenn wir jetzt das ganze Rendern würden. Würde man sehen, das die Texturen verschoben sind. Das liegt daran, das wir Blender noch nicht gesagt haben, das es die UV's verwenden soll. Also geht ihr auf Mapping und unter Coordinates wählt ihr [b]UV [/b]und danach als Map [b]UVMap. [/b]

[attachment=520:use_textur.png]

Und hier das fertig gerenderte Ergebnis (ja, ich war total Kreativ bei der Textur):

[attachment=521:render.png]


[b]Zootec[/b]

Zootec

Zootec

×