• Announcements

    • Lars

      Allgemeine Forenregeln   03/13/2017

      Forenregeln Nimm dir bitte einen Moment um die nachfolgenden Regeln durchzulesen. Wenn du diese Regeln akzeptierst und die Registration fortsetzen willst, klick einfach auf den "Mit der Registrierung fortfahren"-Button. Um diese Registration abzubrechen, klick bitte einfach auf den "Zurück" Button deines Browsers. Wir garantieren nicht für die Richtigkeit, Vollständigkeit und Brauchbarkeit der Nachrichten und sind auch nicht dafür verantwortlich. Die Beiträge drücken die Meinung des Autors des Beitrags aus, nicht zwangsläufig das, wofür die Forensoftware steht. Jeder Nutzer, der denkt, dass ein veröffentlichter Beitrag unzulässig bzw. störend ist, ist aufgefordert uns unverzüglich per E-Mail zu kontaktieren. Wir haben das Recht störende Beiträge zu löschen und bemühen uns, das in einem realistischem Zeitraum zu erledigen (sofern wir beschlossen haben, dass die Löschung notwendig ist). Du akzeptierst, durchgehend während der Nutzung dieses Services, dass du dieses Forum nicht dazu missbrauchen wirst, Inhalte zu veröffentlichen, welche bewusst falsch und/oder verleumderisch, ungenau, beleidigend, vulgär, hasserfüllt, belästigend, obszön, sexuell belästigend, bedrohlich, die Privatsphäre einer Person verletzend oder in irgend einer Art und Weise das Gesetz verletzen. Des Weiteren akzeptierst du, dass du keine urheberrechtlich geschützte Inhalte ohne Erlaubnis des Besitzers in diesem Forum veröffentlichst. Mit dem Klick auf den "Mit der Registrierung fortfahren"-Button, akzeptierst du zudem unsere Datenschutzerklärung und stimmst der Speicherung deiner IP-Adresse und personenbezogenen Daten zu, die dafür benötigt werden, um dich im Falle einer rechtswidrigen Tat zurückverfolgen zu können bzw. permanent oder temporär aus dem Forum ausschließen zu können. Es besteht keine Pflicht zur Abgabe der Einwilligung, dies erfolgt alles auf freiwilliger Basis.   Zusatzinformationen Der Forenbetreiber hat das Recht, Nutzer ohne Angabe von Gründen permanent aus dem Forum auszuschließen. Des Weiteren hat er das Recht, Beiträge, Dateianhänge, Umfrage, Blogeinträge, Galleriebilder oder Signaturen ohne Angabe von Gründen zu entfernen. Mit der Registrierung verzichtest du auf alle Rechte an den von dir erstellten Inhalten, bzw. treten diese an das Unity-Insider.de und Unity-Community.de ab. Dies bedeutet im Klartext, dass das Unity-Insider.de und Unity-Community.de frei über deine Texte verfügen kann, sofern diese nicht wiederum die Rechte anderer verletzen. Es besteht weiterhin kein Anspruch von registrierten Nutzern bzw. ehemaligen registrierten Nutzern darauf, dass erstellte Inhalte und/oder die Mitgliedschaft (User) wieder gelöscht werden (Erhaltung der Konsistenz dieses Forums).   Einwilligungserklärung Wenn du mit der Speicherung deiner personenbezogenen Daten sowie den vorstehenden Regeln und Bestimmungen einverstanden bist, kannst du mit einem Klick auf den Mit der Registrierung fortfahren-Button unten fortfahren. Ansonsten drücke bitte Zurück. Stand: 07.03.2011

Leaderboard


Popular Content

Showing most liked content since 05/22/2017 in all areas

  1. 34 likes
    Es gibt eigentlich keine richtitge Rubrik für dieses Thema, deswegen poste ich es in den Texttutorials, da es am naheliegendsten ist. Weil doch einige Leute nicht alle Begrifflichkeiten aus dem Grafikbereich kennen, weil sie z.B. Programmierer sind und einfach nichts damit zu tun haben, will ich hier mal etwas Licht ins Dunkel bringen es geht hier um das 3D Objekt und was dazu gehört. Jedes 3D Spiel braucht 3D Objekte. Da in Unity nur einige Grundkörper zur Verfügung stehen, muss man ein anderes Programm nutzen um 3D Objekte wie z.B. ein Männchen, Monster, Gegenstände, Häuser oder Fahrzeuge zu erstellen. Alle diese Programme arbeiten ähnlich und wenn das Objekt erst einmal erstellt ist muss man es nach Unity rüber bringen. Hier und da gibt es Schwierigkeiten beim im/export aber darauf will ich nicht eingehen. Ich möchte hier mal mit recht einfachen Worten erklären, was ein 3D Objekt ist, wie es sein sollte, warum man auf gewisse Sachen achten muss und wie sich das mit den Texturen und Animationen verhält. Das 3D Objekt: Jedes 3D Objekt wird aus Flächen gebildet. Auch wenn das Objekt nur aus einer einzigen Fläche besteht, ist es ein 3D Objekt denn diese Fläche wird aus Eckpunkten gebildet und diese Punkte haben Informationen wo sie sich im Raum befinden. Eine Fläche muss mindestens 3 Eckpunkte haben, was ja klar ist, denn nur mit 2 Punkten wäre es eine Linie und keine Flächen. Der Fachausdruck für eine Fläche ist das "Polygon". In den gängigen 3D Programmen wird mit viereckigen Flächen gearbeitet. Es können aber auch mehr Ecken sein. Eine dreieckige Fläche nennt man "Tris". Eine viereckige Fläche nennt man "Quad". Flächen die mehr als 4 Punkte haben nennt man "N-Gons" (das "n" steht für irgendeine Zahl über 4), denn sobald diese Flächen über 4 Eckpunkte besitzen ist das 3D Programm gefordert und es macht keinen Unterschied mehr, wieviele Punkte es nun wirklich sind. Ganz wichtig zu wissen ist die Tatsache, dass fast in jedem Programm aus den Quads und n-Gons intern Dreiecke bildet. Unity arbeitet auch nur mit Dreiecken und die Objekte werden schon beim Import gewandelt. Es gibt da aber Schwierigkeiten bei den n-Gons! Ist bei es bei einem Quad einfach die Fläche zu unterteilen um 2 Tris zu bekommen (kann ja nur von Punkt 1 nach Punkt 3 oder aber von Punkt 2 nach Punkt 4 geschnitten werden) so gibt es bei n-Gons viele Möglichkeiten des Schneidens. Je nachdem können da gleichmäßige Dreiecke entstehen oder aber sehr große und ganz spitze. Das wirkt sich später auf das Aussehen des Objektes aus und kann ungewünschte Ergebnisse erzielen. Deswegen auf n-Gons verzichten und einfach selber den Schitt setzen um maximal Quads zu haben. Wie viele Polygone ein Objekt hat oder haben sollte hängt davon ab was ich in meinem Spiel zeigen will, welche Shader ich nutzen will, wie beleuchtet wird, wieviel Performance mir zur Verfügung steht und wieviele Polygone insgesamt in der Szene zu sehen sind. Eine ganz normale Schachtel, die keine abgerundeten Kanten hat, braucht wirklich nur 12 Polygone (6 Seiten a 2 tris). Jede größere Unterteilung wäre Verschwendung. Würde diese Schatel sich nicht bewegen lassen, bliebe also immer am selben Fleck liegen, dann kann man sogar die 2 Polygone den Bodens weg lassen. Habe ich eine Spielfigur, welches viele Details haben soll, da es nah zu sehen ist, brauche ich auch ein Mindestmaß an Polygonen. Wieviele das werden ist schwer zu sagen, da man vieles auch mit Texturen lösen kann. So braucht man leichte Unebenheiten der Kleidung oder Rüstung nicht ausmodellieren sondern kann das über Texturen vorgaukeln. Aber wie auch immer, solch ein Modell wird schon mehr als 1000 Polygone haben. Je realistischer das Ganze werden soll umso mehr muss das Polygon bringen. Bei modernen Spielen, die sich nicht mehr auf einfachen Grafikkarten spielen lassen, haben die Figuren auch schon mal mehr als 10.000 Polygone. Aber auch da wird geschaut, wo man bei anderen Objekten die Polygone einsparen kann, sodass die Summe an Polygonen in der Szene nicht zu hoch wird. Die Animation mit Bones: Um eine Spielfigur laufen zu lassen, muss man dem 3D Objekt ein Skelett verpassen. Man legt fest, welche Punkte des 3D Objektes mit welchen bones (Knochen) interagieren. An einem Bein will ich das mal verdeutlichen. Dieses Bein hat jetzt nur einen Oberschenkelknochen und einen Unterschenkelknochen. Wenn ich den Oberschenkelknochen an seinem Gelenk drehe, dreht sich das ganze Bein. Drehe ich den Unterschenkelknochen, bleibt der Oberschenkel wie er ist, aber der Unterschenkel dreht sich über das Kniegelenk. Damit das so ist, muss ich die Knochen an das Mesh binden. Ich lege also fest, welche Punkte sich in welcher Stärke mit dem zugehörigen Knochen bewegen sollen. Somit hat der Oberschenkelknochen die Punkte des Oberschenkels und der Unterschenkelknochen die Punkte des Unterschenkels. Am Knie teilen sich beide Knochen die Punkte. WIe stark welcher Knochen einen Punkt bewegen darf, nennt man Wichtung. Jeder Punkt muss am Schluß zu 100% gewichtet sein. Egal ob nur ein Knochen auf den Punkt einwirkt oder mehrere sich die Einwirkung teilen. Damit das Knie schön rund bleibt, wenn es einknickt, reicht es nicht nur direkt am Knie Punkte zu haben. Man braucht auch noch Loops (Unterteilungen im Mesh) etwas oberhalb und etwas unterhalb vom Knie. Der Oberschenkelknochen übergibt den Punkten unterhalb nur wenig Wichtung und beim Unterschenkel ist es genau umgekehrt. Dadurch kann man das Knicken etwas weicher gestalten lassen und es kommt nicht zu unschönen Deformationen. Ist jeder Punkt zu 100% gewichtet, kann man das Objekt animieren. Dafür macht man quasi Fotos der Position der Punkte in der Zeitleiste. Am Schluss hat man eine gewisse Animationsdauer und während dieser Dauer mehrere Fotos von den Positionen der Punkte gemacht. Dieses wird auch nach Unity importiert und kann dort abgespielt werden. Dieses 3D Objekt hat jetzt ein Skinned Mesh! Unity weiss also, dass die Punkte nicht starr sind sondern sich innerhalb der Animation bewegen. In Unity kann eingestellt werden, wieviele bones jetzt wirklich auf so einen Gelenkbereich wirken sollen. Voreingestellt ist glaube ich 2. Je mehr Bones auf das Mesh wirken, umso mehr muss Unity die Sache berechnen. Sowieso braucht jeder Bone Performance, weswegen es auch gut ist so wenig Bones wie möglich zu nutzen. Es gibt noch weitere Einstellungsmöglichkeiten, die etwas Performance zurück bringen, denn wenn z.B. ein Skinned Mesh nicht im Bild ist, muss es auch nicht unbedingt animiert werden. Texturen, Shader und die UV Map: 3D Objekte sollen ja auch nach etwas aussehen und somit brauchen die ein Material. Das Material ist erstmal nur ein Container in dem ein Shader eingestellt ist. Dieser Shader kann unterschiedliche Kanäle haben um einen Farbanteil, eine Transparenz, eine Textur und einen Glanz/Spiegelung zu haben. Habe ich jetzt wieder mal eine Schachtel, die z.B. wie Holz aussehen soll und zusätzlich einen Aufdruck auf einer der Seiten hat, reicht mir die Farbe braun nicht aus. Ich muss einen Shader nutzen, in dem ich eine Textur einladen kann. Diese Textur ist erstmal eine ganz normale Grafik. Hätte ich z.B. ein Foto von einer Holzmaserung und würde das jetzt dem Shader zuweisen, so würden alle Seiten der Kiste mit dieser Holzmaserung bestückt werden. Da nichts weiter an Informationen vorliegt, wird Unity von sich aus auf jede Seite die Maserung drauflegen und skalieren. Man sieht das, denn die Ausrichtung der Maserung ist nicht immer richtig. Mehrere Seiten sind verzerrt. Um das zu umgehen, brauche ich eine UV Map. Man stelle sich ein Blatt Papier vor, auf das ich die Kiste lege. Die Unterseite der Kiste belegt jetzt einen gewissen Bereich des Papieres. Würde ich an den Eckpunkten der Kiste jetzt echte Punkte auf das Papier malen, hätte ich einen Bereich definiert, der später der untenliegenden Seite der Kiste zugewiesen würde. Die Kiste könnte ich jetzt über eine Kante abrollen und wieder Punkte auf das Papier bringen, wo die Ecken der neuen Seite der Kiste sind. Ihr würdet sehen, dass ich nur 2 neue Punkte setzen muss, denn die ersten 2 Punkte sind die gleichen 2 Punkte der Unterseite der Kiste. Das würde ich mit allen Seiten der Kiste so machen und hätte am Schluss 6 Bereiche auf meinem Blatt Paier definiert. Ich hätte eine UV Map erzeugt. So ist das auch bei den 3D Objekten. Ist eine UV Map erzeugt worden hat das Objekt eine Information darüber, welches Polygon welchen Bereich meiner Grafik zugewiesen bekommt. Da es ja weiß welche Punkte wo auf dem Blatt liegen. Mit einem Grafikprogramm könnte ich jetzt eine Holztextur in die richtigen Bereiche der Grafik einfügen und dann auch zusätzlich den Aufdruck, den die Kiste später auf einer Seite haben soll, dazu malen. In Unity würde das 3D Objekt jetzt die Textur mit Hilfe der Informationen genau richtig platzieren. Ich hätte eine Kiste, die von allen Seiten anders aus sehen kann, ganz so, wie ich es wollte. Das erzeugen einer UV Map geht leider nur bedingt automatisch und braucht einiges an Handarbeit. Viele 3D Programme haben diese Funktion zum Erzeugen schon dabei. Es gibt aber auch unabhängige Tools. In Unity braucht jedes neues Material in der Szene Performance. Deswegen ist es gut, wenn sich viele Objekte ein Material (Textur) teilen. So eine Grafik, wo z.B. viele Button Grafiken nebeneinander liegen würden, nennt man Texturatlas. Jeder Button würde wissen, welchen Teil des Textruenatlas er zeigen soll. Man kann es direkt in Unity einstellen, wenns nur 2D ist, oder aber die Objekte haben alle unterschiedliche UV Maps, die immer nur einen Bereich der Großen Texture benötigen. Um Unebenheiten auf Oberflächen zu erzeugen, muss man sie nicht modellieren, nein man kann auch eine NormalMap oder einen Bumpmap (Höhenkarte) dafür nutzen. Das ist wieder so eine UV Textur, die jetzt aber nicht die Farbe des Objektes beinhaltet, sondern Höheninformationen. Bei der Bumpmap wird das mit Graustufen gelöst, wobei weiß hoch bedeutet und schwarz tief. Die Normalmap ist 3 Farbig und kann dadurch auch seitliche Erhebungen darstellen. Es ist viel performanter Unebenheiten über die Textur zu erzeugen anstatt über die echte Geometrie zu gehen. Auch wenn so ein Shader wiederum etwas performance braucht. Natürlich wird dadurch alles nur vorgegaukelt und es scheint, als hätte die Oberfläche eine Struktur. Sie ist aber noch glatt und wenn die Struktur auch Auswirkung auf andere Objekte haben soll, kann das nicht mit der Textur lösen.
  2. 14 likes
    In dieser Tutorial reihe wollen wir uns mithilfe von Unity und der Programmiersprache C# einen Space Shooter basteln. Dabei werde ich euch sanft mit Unity und C# vertraut machen. Deshalb ist diese Reihe sowohl für absolute Anfänger in Sachen Unity als auch im Programmieren geeignet. Im ersten Teil wollen wir unseren Grundstein legen, wir wollen unser Spieler Raumschiff mithilfe der Tastatur steuern. Das Spielerraumschiff stellen wir uns zuerst als einfachen Würfel zur Verfügung. In einem späteren Tutorial werden wir diesen Würfel durch komplexere Gebilde austauschen. Folgendes soll unser Raumschiff in der ersten Version können: - Beschleunigen wenn wir die Vorwärtstaste drücken - Negativ Beschleunigen wenn wir die Rückwärtstaste drücken - Nach links drehen wenn wir die Linkstaste drücken - Und nach rechts drehen wenn wir die Rechtstaste drücken Wir beginnen das ganze indem wir uns ein neues Projekt in Unity erstellen, komplett leer ohne Schnickschnack. Wählt dazu, innerhalb von Unity den Menüpunkt "File"->"New Project.." aus und wählt im darauf folgenden Dialog einen geeigneten Namen und Speicherort für euer Projekt aus. Selektiert zusätzlich die Option "3D" unter dem Punkt "Setup defaults to" im unteren Bereich des Dialoges. Klickt danach auf OK. Wir erstellen nun unser Raumschiff. Dazu wählen wir im Menü "GameObject"->"Create Other"->"Cube". Dies erzeugt uns unser erstes GameObject, einen Würfel mit dem Namen "Cube". Da der Name "Cube" wenig aussagt benennen wir ihn kurzerhand in "Spieler Raumschiff" um: Wählt dazu den Würfel in der Ansicht die "Hierarchy" genannt wird (ganz links) aus. Dann könnt ihr im Fenster welches "Inspector" genannt wird (ganz rechts) den Namen ändern indem ihr in das Textfeld mit dem Inhalt "Cube" klickt und dort den Text "Spieler Raumschiff" eintippt. Die Anführungszeichen bitte weglassen. Ihr könnt zum Umbenennen eines GameObjectes auch, nachdem ihr es in der "Hierarchy" Ansicht ausgewählt habt, die F2 Taste drücken und dort den Namen eintippen. Das Spieler Raumschiff ist nun noch etwas ungünstig positioniert und um das zu ändern wählen wir es wie bereits getan aus und ändern in der "Inspector" Ansicht die Position. Dies kann gemacht werden indem ihr unter "Position" alle 3 Werte (X, Y und Z) auf 0 ändert. Um auch die Kamera etwas passender zu positionieren und zu drehen wählen wir nun auch das "Main Camera" GameObject aus und ändern im Inspektor die Position auf: X = 0, Y = 6 und Z = -2 Sowie die Rotation auf X = 70 Dadurch schaut unsere Kamera auf unser Raumschiff. Speichert nun die Scene ab indem ihr Strg+S drückt. Unity wird euch darauf hin fragen wie die Scene heißen soll. Ich habe meine Scene "Scene0" genannt. Wärend der Entwicklung solltet ihr darauf achten immer wieder Strg+S zu drücken um eure Zwischenstände beim verändern der Scenen auch zu speichern. Nichts ist ärgerlicher als zuvor gemachte Änderungen wiederholen zu müssen. Weil Unity zB abgestürzt ist. Damit sind die Vorbereitungen abgeschlossen und wir können uns nun ein Script erstellen welches unser Raumschiff steuert. Dazu klicken wir mit der rechten Maustaste in den "Assets" Bereich (ganz unten) und wählen den Kontextmenüpunkt "Create"->"C# Script" Anschließend wählen wir als Namen für dieses Script "PlayerControler". "PlayerControler" deshalb weil dies unser Script ist welches das Spieler Schiff kontrolliert. Wir werden in dieser TutorialReihe englische Bezeichner verwenden sofern es sich um das Programmieren handelt. In Unity ist der Name des Scriptes gleichbedeutend mit dem Klassennamen. Dazu aber gleich mehr. Um nun dieses Script zu editieren könnt ihr es doppelt anklicken. Darauf hin sollte sich ein Editor öffnen und ihr seht folgendes: using UnityEngine; using System.Collections; public class PlayerController : MonoBehaviour { // Use this for initialization void Start () { } // Update is called once per frame void Update () { } } Die ersten beiden Zeilen (die die mit using beginnen) helfen dabei uns einige Grundfunktionen zu geben die wir in unserem Script verwenden können. using UnityEngine gibt uns alle Funktionen die direkt mit Unity zu tun haben. using System.Collections gibt uns einige Hilfsmittel um Ansammlungen von Daten zu verwalten, zB eine Reihe von Werten die wachsen kann und aus der Elemente entfernt werden können. Ausserdem stellt es uns etwas bereit was wir benötigen sobald es um Coroutinen geht. Die nächste Zeile definiert den Namen unseres Scriptes (PlayerController) und gibt an was genau es sein soll (class, eine Klasse) und von wem es einige Grundfunktionen bekommen soll (MonoBehaviour). Jedes Script welches wir auf GameObjects legen können leitet von MonoBehaviour ab. "leitet" bedeutet hier dass unser Script zugleich selbst ein MonoBehaviour ist und damit einige Grundfunktionen beherbergt die dieses MonoBehaviour selbst bereit stellt. Stellt euch einfach ein Auto vor. Ein Auto leitet zB von Fahrzeug ab und kann dadurch gefahren werden. Die Zeile mit void Start() bedeutet dass wir hier eine Methode definieren die Start heißt. Im Bezug auf Unity bedeutet dies dass diese Methode automatisch von Unity aufgerufen wird wenn das Script auf ein GameObject liegt und die Scene betritt. Innerhalb des Bereiches der durch { und } eingegrenzt ist können wir schreiben was unser Script machen soll wenn es startet. Der durch { und } eingegrenzte Bereich wird Scope genannt. Die Zeile Update ist ähnlich wie Start, nur wird diese Update Methode nicht aufgerufen wenn unser Script die Welt betritt sondern in jedem Framedurchgang wenn unser GameObject und Script aktiv sind. Frame bedeutet der Moment in dem ein Bild fertig gezeichnet wurde. Je schneller ein Computer desto mehr Frames in einer Sekunde können erreicht werden. In der Update Methode können wir überprüfen ob der Spieler eine Taste gedrückt hat und dies werden wir nun auch tun. Mithilfe der Input Klasse von Unity können wir abfragen ob eine Taste gedrückt wurde, wir erweitern deshalb die Update Methode um folgendes: if (Input.GetKey(KeyCode.UpArrow)) { print("Power!"); } Denkt daran: Schreibt das ganze zwischen { und } der Update Methode. Das if ist ein Schlüsselwort von C# welches besagt dass hier eine Abfrage statt finden soll und das innerhalb der Klammern ist die Bedingung die dazu erfüllt sein muss damit der darauf folgende Bereich (Scope) innerhalb der { und } Zeichen ausgeführt wird. Input gibt eine Klasse an welche die GetKey Methode bereit stellt. Die GetKey Methode benötigt einen Parameter der besagt welche Taste geprüft werden soll und dies definieren wir mit KeyCode.UpArrow. Input.GetKey liefert einen bool Wert zurück. bool bedeutet dass es 2 verschiedene Werte gibt, entweder true, was wahr oder ja bedeutet, oder false, welches unwahr, nein oder falsch bedeutet. Bedingungen erwarten immer ein bool welches sie auswerten können. Nur wenn dieses bool true ist wird die Ausführung des Scopes vom if gestartet. Die print Methode gibt den String aus der ihr als Parameter übergeben wurde. Ein String ist eine Zeichenkette, ein Text. Wenn wir direkt einen Text angeben wollen so müssen wir ihn innerhalb der Anführungszeichen schreiben. Dann speichern wir das ganze und wechseln zurück zu Unity. Unity wird nun dieses von uns gespeicherte Script kompilieren (in Maschinencode umwandeln) und uns auf eventuelle Fehler hinweisen. Wenn alles erfolgreich kompiliert wurde können wir unser Script auf unser Spieler Raumschiff zuweisen. Dazu wählt das Spieler Raumschiff aus und klickt im unteren Inspektor Bereich (möglicherweise müsst ihr nach unten scrollen) auf den Knopf "Add Component" wählt dann "Scripts"->"Player Controller" aus. Unser Script sollte nun unserem Raumschiff zugewiesen worden sein. Das zuweisen ist auch möglich indem man das Script aus dem Asset Bereich auf das Raumschiff schiebt. Jetzt können wir auf den Play Knopf drücken (das Dreieck im oberen Bereich von Unity). Wenn wir nun die nach Oben Taste drücken sollte im unteren linken Bereich von Unity der Text "Power!" stehen. Glückwunsch du hast soeben dein erstes Script geschrieben und es einem GameObjekt zugewiesen und siehst nun noch wie es agiert. Das Raumschiff bewegt sich aber dennoch nicht, was ziemlich öde ist und schleunigst geändert werden sollte. Unser Raumschiff benötigt dazu die Möglichkeit physikalisch bewegt zu werden, dies geht indem wir dem Raumschiff die Rigidbody Komponente (Komponenten sind Scripte!) zuweisen. Dazu klicken wir wieder im Inspektor den Knopf "Add Component" aus und benutzen "Physics"->"Rigidbody". Im Inspektor erscheint nun ein zusätzlicher Bereich mit dem Titel "Rigidbody". Hier müssen wir auch noch einige Änderungen vornehmen, zB müssen wir dafür sorgen dass unser Raumschiff nicht nach unten fällt. Dazu deaktivieren wir die Checkbox "Use Gravity". Nun können wir mithilfe des Rigidbodies unser Raumschiff steuern. Dazu gehen wir wieder in unser PlayerController Script und entfernen die Zeile mit dem print("Power!") und ersetzen die Zeile durch die Anweisung welche unser Raumschiff beschleunigt: rigidbody.AddForce(transform.forward); rigidbody ist eine Abkürzung die es uns erlaubt ohne extra Code direkt auf unser Rigidbody zuzugreifen, diese Abkürzung gibt es in ähnlicher Form auch für andere Komponenten, zB der Transform Komponente (transform). AddForce ist eine Methode der Rigidbody Komponente die wir aufrufen können um etwas Kraft auf das rigidbody auszuüben. Da wir es vorwärts, entlang der Blickrichtung des Objektes beschleunigen wollen, benutzen wir transform.forward als Parameter. transform ist die Abkürzung auf die Transform Komponente welche Position, Richtung, Skalierung des GameObjektes beinhaltet. Und forward ist ein Richtungsvector der dorthin zeigt wo das GameObjekt "hin blickt". Wenn wir nun das Script speichern und uns das Resultat in Unity ansehen (Play drücken) dann können wir sehen wie sich unser Raumschiff bewegt wenn wir die nach Oben Taste drücken. Leider ist diese Beschleunigung stark von der Anzahl der Frames pro Sekunde abhängig. Was bedeutet das auf schnelleren Computer die Beschleunigung höher ist als auf langsameren Computern. Ausserdem können wir die Beschleunigung nicht genauer regeln und entspricht immer der Länge des Richtungsvectors. Um das zu ändern erweitern wir unser Script wieder ein wenig. Als aller erstes machen wir die Beschleunigung unabhängig von der Anzahl der Frames pro Sekunde: aus rigidbody.AddForce(transform.forward); wird deswegen: rigidbody.AddForce(transform.forward * Time.deltaTime); Time ist die Klasse von Unity die einiges an Operationen und Variablen bereit stellt die etwas mit der Zeit zu tun haben. deltaTime zB ist die Zeit in Sekunden die der letzte Frame benötigte. Indem wir transform.forward mit diesem Wert multiplizieren bleibt unsere Beschleunigung auch dann konstant wenn wir einen sehr langsamen oder sehr schnellen Computer verwenden. Wenn wir nun das ganze in Unity ausprobieren werden wir allerdings ein unangenehmes Wunder erleben. Unser Würfel bewegt nur sehr langsam. Deswegen werden wir nur die Kraft erhöhen. Dazu erweitern wir unser Script indem wir innerhalb der Klassendefinition über void Start folgendes schreiben: public float Acceleration = 10.0f; public besagt dass die float Variable öffentlich erreichbar ist, also jeder der unser Script zu fassen bekommt diesen Wert lesen und/oder verändern kann. Dies besagt zB auch dass dieser Wert im Inspektor von Unity angepasst werden kann. Acceleration ist der Name unserer Variable und float der Datentyp. float gibt an dass wir einen Zahlentyp wollen der Kommawerte darstellen kann. Ein Zahlentyp der nur ganze Zahlen darstellen kann wäre zB int. Wir verwenden unsere Acceleration Variable nun indem wir unser Zeile in der wir Kraft auf das Rigidbody wirken lassen erweitern und zwar: rigidbody.AddForce(transform.forward * Time.deltaTime * Acceleration); Wir multiplizieren also unsere Frame unabhängige Kraft mit unserer Variable. Dies ermöglicht es uns nun anzugeben wie Stark die Beschleunigung ist. Wenn wir nun wieder das ganze innerhalb von Unity testen sehen wir dass unser Würfel sich schneller bewegt als zuvor. Wir können diesen Wert anpassen indem wir im Inspektor nach unsererm Script Player Controller suchen und dort den Eintrag mit dem Namen "Acceleration" ändern. Unser Script sollte aktuell wie folgt aussehen: using UnityEngine; using System.Collections; public class PlayerController : MonoBehaviour { public float Acceleration = 10.0f; // Use this for initialization void Start () { } // Update is called once per frame void Update () { if (Input.GetKey(KeyCode.UpArrow)) { rigidbody.AddForce(transform.forward * Time.deltaTime * Acceleration); } } } Da wir nun schon das Beschleunigen haben werden wir nun noch das Abbremsen hinzufügen. Das ganze ist ziemlich identisch zum Beschleunigen und ist eine Ideale Übung zum selber probieren des bereits erlernten. Wenn ihr euch ausgetobt habt oder einfach nur weiter machen wollt, hier ist der Code welcher auch die negative Beschleunigung beinhaltet: using UnityEngine; using System.Collections; public class PlayerController : MonoBehaviour { public float Acceleration = 10.0f; public float Deacceleration = 10.0f; // Use this for initialization void Start () { } // Update is called once per frame void Update () { if (Input.GetKey(KeyCode.UpArrow)) { rigidbody.AddForce(transform.forward * Time.deltaTime * Acceleration); } if (Input.GetKey(KeyCode.DownArrow)) { rigidbody.AddForce(-transform.forward * Time.deltaTime * Deacceleration); } } } Was nun noch fehlt ist das Drehen unseres Raumschiffes. Dies funktioniert auf leicht ähnliche Weise wie das Beschleunigen und Abbremsen. Deswegen legen wir uns wieder eine Variable an welche angibt wie schnell die Drehung statt finden soll. public float RotationAcceleration = 30.0f; Als Nächstes müssen wir nun noch auf den Tastendruck reagieren der das Drehen veranlassen soll, ich werde mit dem nach rechts drehen anfangen was relativ einfach auf eine links Drehung erweitert werden kann: if (Input.GetKey(KeyCode.RightArrow)) { rigidbody.AddTorque(0, Time.deltaTime * RotationAcceleration, 0); } So wie AddForce fügt AddTorque etwas an unser Rigidbody. und zwar ein Drehmoment. Wir beschleunigen das Drehmoment unseres Rigidbdies um die Y Achse. Wenn wir das Ganze nun in Unity testen können wir unseren Cube schon vor der Kamera bewegen. Nur fühlt es sich momentan so an als würden wir auf dem Eis laufen. Dies liegt daran dass wir direkt Kräfte auf unseren Würfel anwenden, was ansich realistisch ist, aber leider das ganze schwieriger Steuerbar macht. Die Lösung dazu? Wir steuern durch das Beschleunigen oder Abbremsen die Kraft die unser Antrieb pro Frame automatisch auf den Würfel anwenden soll. Dazu fügen wir eine Reihe weiterer Variablen ein die dafür zuständig sind die maximale und minimale Kraft zu regeln als auch die momentane Kraft die unser Antrieb generieren soll zu speichern: public float MaxForce = 1000.0f; public float MinForce = -200.0f; public float CurrentForce = 0.0f; Nachdem wir das nun haben müssen wir natürlich auch die Handhabung der nach Oben und Unten Tasten ändern. Wir ändern hierbei jeweils die AddForce Zeilen indem wir den Frame unabhängigen Acceleration Wert zu CurrentForce hinzu addieren und den Deacceleration Wert abziehen. if (Input.GetKey(KeyCode.UpArrow)) { CurrentForce += Time.deltaTime * Acceleration; } if (Input.GetKey(KeyCode.DownArrow)) { CurrentForce -= Time.deltaTime * Deacceleration; } += bedeutet hierbei dass der Wert rechts von += zu den Wert links vom += addiert werden soll. Das ganze ist gleich zu a = a + b -= bedeutet dass der Wert abgezogen anstatt hinzugefügt wird. Jetzt haben wir die CurrentForce zwar angepasst aber noch kann sie sowohl zu hoch als zu niedrig werden und verwenden tun wir diesen Wert ebenso wenig. Daher grenzen wir CurrentForce zuerst ein. Dies geht indem wir die Clamp Methode der Mathf Klasse von Unity verwenden. Schreibe folgendes ans Ende der Update Methode: CurrentForce = Mathf.Clamp(CurrentForce, MinForce, MaxForce); Mathf.Clamp begrenzt den Wert im ersten Parameter innerhalb von den Wert des zweiten und dritten Parameters und gibt das Resultat wieder zurück. Damit ist sicher gestellt dass die CurrentForce niemals größer oder kleiner unserer Grenzwerte wird. Nun müssen wir CurrentForce nur noch irgendwie auf unser Raumschiff angewendet bekommen. Hierfür benutzen wir einfach unser rigidbody.AddForce wieder. Wir multiplizieren CurrentForce mit der Zeit die der letzte Frame zum ausführen brauchte und der Richtung unseres Raumschiffes: rigidbody.AddForce(CurrentForce * Time.deltaTime * transform.forward); Der aktuelle Code des PlayerController Scriptes sollte nun so aussehen: using UnityEngine; using System.Collections; public class PlayerController : MonoBehaviour { public float Acceleration = 100.0f; public float Deacceleration = 100.0f; public float RotationAcceleration = 30.0f; public float MaxForce = 1000.0f; public float MinForce = -200.0f; public float CurrentForce = 0.0f; // Use this for initialization void Start () { } // Update is called once per frame void Update () { if (Input.GetKey(KeyCode.UpArrow)) { CurrentForce += Time.deltaTime * Acceleration; } if (Input.GetKey(KeyCode.DownArrow)) { CurrentForce -= Time.deltaTime * Deacceleration; } if (Input.GetKey(KeyCode.RightArrow)) { rigidbody.AddTorque(0, Time.deltaTime * RotationAcceleration, 0); } if (Input.GetKey(KeyCode.LeftArrow)) { rigidbody.AddTorque(0, -Time.deltaTime * RotationAcceleration, 0); } CurrentForce = Mathf.Clamp(CurrentForce, MinForce, MaxForce); rigidbody.AddForce(CurrentForce * Time.deltaTime * transform.forward); } } Das Raumschiff beschleunigt nun immer mit der aktuellen Antriebskraft in die aktuelle Schiffsrichtung. Das fühlt sich nun schon alles viel besser an als vorher, die Steuerung ist leichter zu beherrschen, aber etwas fehlt noch.. Spielt ein wenig mit den Werten Im Inspektor der Rigidbody und PlayerController Komponenten herum um ein leichtes Gefühl dafür zu bekommen wie sich jeder Wert auswirkt. Gute Werte sind zB folgende: PlayerController: Acceleration 100 Deacceleration 50 Rotation Acceleration 100 Max Force 100 Min Force -20 Rigidbody: Mass 1 Drag 0 Angular Drag 10 Das war es für den ersten Teil der Tutorial Reihe. Im nächsten Teil werden wir die Kamera so umbauen dass diese immer auf das Spieler Raumschiff gerichtet ist. Feedback ist gern gesehen. Teil2: http://forum.unity-c...__fromsearch__1
  3. 12 likes
    Gerade unerfahrene Leute haben starke Bedenken, wenn es um die Performance in Unity geht. Sie haben aus unterschiedlichen Quellen gehört, dass Dies oder Jenes schlecht sein soll und jetzt gehen sie total ängstlich an das Projekt heran. Denen möchte ich aber sagen: Macht euch nicht verrückt! Denn wie immer ist alles eine Frage von der Menge an leistungsfressenden Dingen, dem gewünschten Resultat und der Hardware, auf der das Spiel laufen soll. Viele Dinge beziehen sich auf Hardware, die kaum noch genutzt wird, wie z.B. das iPad1, welches nicht mehr als 120MB Ram für das Spiel zur Verfügung gestellt hatte. Oder aber es bezog sich auf veraltete Techniken bei denen Unity schon längst performatere Dinge gebastelt hat, wie die unterschiedlichen Lighting-Arten. Ich will mal versuchen, die einzelnen Bereiche abzuarbeiten. Das Grafische: Alles was ich sehen kann kostet Leistung. Mal mehr, mal weniger! Je mehr Realismus ich in ein Spiel rein bringen will umso mehr Leistung wird verbraucht. Denn jedes Poygon, jedes Licht, jede Schattenberechnung und jedes Material verbraucht etwas. Diese Dinge belasten meist die Grafikkarte, denn die ist es, die das ja anzeigen und vorher berechnen muss. Habt ihr ein Spiel mit üppiger Szenerie und schaut euch zur Laufzeit mal den Profiler an, dann seht ihr, dass der Grafikbereich die meiste Leistung schluckt. Unity ist von sich aus schon bemüht, sparsam zu sein und kann gleichartige Dinge zusammen fassen, wenn sie z.B. das gleiche Material haben und auch vom selben Licht bestrahlt werden. Das reduziert die Drawcalls, die einen Großteil der Performance ausmachen. Man hat die Möglichkeit Beleuchtungen und Verschattung vor zu berechnen und die Ausleuchtung einer Szene zu backen. Das reduziert ganz massiv die Last auf der Grafikkarte, kostet aber Speicherplatz und natürlich geht die Dynamik der Beleuchtung dadurch flöten. Aber was bringt es, wenn man genau diese Dynamik haben will. Genau, es bringt nichts! Mit Shadern sieht es genauso aus. Ein einfacher Shader mit nur einer Textur ist recht sparsam. Er gibt aber auch nicht viel her. Schöner ist er, wenn er glänzen kann, transparent ist, erhaben ist und eben realistisch aussieht. Will ich das haben muss ich ihn auch einsetzen. Die Geometrie eines Körpers sollte natürlich so sparsam wie möglich aufgebaut sein. Die Zeit, wo ein Oberarm aus 12 Polygonen besteht, ist aber längst vorbei. Ich kann zwar vieles mit einer NormalMap simulieren, aber wenn die Ränder des Polygons zu eckig sind, sieht man das und es wirkt einfach nicht. Also auch da muss ich mich den Qualitätsansprüchen stellen und mehr Polygone nutzen. Ich kann sogar DirectX11 einsetzen und aus wenigen Polygonen eine Million Polygone machen. Ja, aber eben nur auf einer Grafikkarte, die das auch kann. Egal, wollt ihr die Technik einsetzen dann funktioniert es eben auch nur auf einer Hardware, die das auch kann. Lasst euch also nicht zu stark einschränken. Ihr werdet natürlich irgendwann merken, dass das Ganze zu viel geworden ist und die Framerate einbricht. Meist lässt sich das aber mit etwas geänderter Beleuchtung, LOD oder anderen Dingen schon wieder beheben. Da aber niemand genau sagen kann, wo die Grenzen liegen werden, bringt es auch nichts, wenn man vorher seitenweise darüber diskutiert. Die Physik: Die physikalischen Berechnungen kosten Leistung, die der Prozessor bringen muss. Und es ist klar, dass viele Berechnungen auch viel Leistung kosten. Trotzdem ist das jetzt kaum ein Bereich, wo man "extrem" sparen muss. Unity kann ohne weiteres mehrere 100 Objekte gleichzeitig physikalisch berechnen. Einfach schauen, dass nur die Objekte einen Rigidbody bekommen, die auch einen haben müssen weil sie der Schwerkraft oder anderen Forces folgen sollen und/oder Kollisionen auswerten sollen. Collider so einfach wie möglich halten und lieber einen Box-Collider mehr nehmen, als einen Mesh Collider zu nutzen. Meist muss es nämlich gar nicht ganz genau sein. Merkt eh keiner. Der Code: Ja, hier kann und muss man aufpassen. Denn wenige Kleinigkeiten können das System ganz schön verlangsamen. Aber macht euch auch hier nicht verrückt. Denn auch hier ist alles eine Frage der Dosis. Ein paar extreme Dinge will ich aber mal aufzählen. Als Faustregel gilt, dass jede Unity-eigene Funktion, die im Script steht, auch durchlaufen wird. Egal, ob innerhalb dieser Funktion etwas drin steht oder nicht. Die OnGUI Funktion ist die hungrigste, weswegen man so wenig Scripte wie möglich mit dieser Funktion bestücken sollte. Legt also nur die Funktionen an, die auch wirklich nötig sind, auch wenn das einsparpotential hier (außer bei der OnGUI) nur gering ist. Immer wenn aus einem Script heraus ein anderes Objekt oder dessen Komponente gesucht wird, kostet es Performance. Es hilft aber nichts. Manchmal muss man einfach nach anderen Objekten oder Komponenten suchen. Um jedoch so wenig Leistung wie möglich zu verbrauchen, sollte man nur einmal danach suchen und einfach die Referenz des Gesuchten in eine Variable speichern. Manche Befehle sind recht hungrig, wie z.B. die Entfernungsmessung über Vector3.Distance. Da sollte man sich überlegen, ob denn wirklich in jedem Frame die Entfernung gemessen werden muss oder reicht es vielleicht auch, wenn es nur wenige Male pro Sekunde passiert. So eine Messung würde ja in jedem Frame eine gewisse Grundlast verursachen, die nicht unbedingt sein muss. Und gerade wenn viele Objekte viele Entfernungsmessungen machen, ist es sinnvoll das Ganze in der Zeit aufzuteilen um die Grundlast zu verringern. So ist das natürlich auch mit komplexen Berechnungen die in einer Schleife ausgeführt werden. SendMessage ist ein teurer Befehl, der an ein gewisses Objekt und an all seine Komponenten etwas sendet. Egal ob die Komponente damit etwas anfangen kann, oder nicht. Diesen Befehl sollte man wirklich nur sparsam nutzen. Will ich einem anderen Script jedes Frame etwas mitteilen, dann ist dieser Befehl dafür total ungeeignet. Für ein einmaliges oder seltenes Senden ist er aber voll ok. Für das ständige Übergeben von Informationen bietet sich an, die Komponente, also das andere Script, vorher zu suchen und als Referenz in eine Variable zu speichern. Jetzt kann ich auf alle Public Variablen oder Funktionen des anderen Scripts zugreifen und sie aufrufen oder manipulieren. Das kostet nicht oder kaum mehr, als wenn es eine Variable des eigenen Scripts wäre. Alles das, was ich da aufgezählt habe macht sich erst ab einer gewissen Menge bemerkbar. Je schwächer die Hardware ist, desto früher merkt man es. Nur weil etwas viel Performance kostet, müsst ihr nicht darauf verzichten. Ihr solltet aber weise damit umgehen. Nur weil ich etwas jedes Frame tue, muss das nicht schlecht sein. Es kommt halt immer darauf an, was ich da tue und wie viele Objekte das gleichzeitig machen. Macht euch vorher Gedanken, was ihr für euer Spiel alles braucht und wie ihr das am besten lösen könnt. Aber lasst euch durch diese Planung nicht blockieren. Fangt einfach an. Vieles ist im Nachhinein leicht änderbar. Spiele, die auf einem PC schön aussehen sollen und können, können nicht unbedingt genauso für ein Handy übernommen werden. Es macht aber nicht unbedingt Sinn, sich auf den kleinsten gemeinsamen Nenner zu einigen. Manchmal muss man einfach mehrere Versionen für ein und das selbe Spiel bauen, weil die Qualitysettings nicht ausreichen werden. Fazit: Es ist also alles gar nicht so schlimm!
  4. 8 likes
    Willkommen im 2ten Teil der Tutorial Reihe. In diesem Teil wollen wir uns näher mit der Kamera beschäftigen. Bisher zeigt diese immer nur starr auf den Ursprungspunkt der Szene. Besser wäre es wenn die Kamera immer unser Raumschiff verfolgen würde. Wir beginnen damit indem wir ein neues Script schreiben. Erstellt daher wie im ersten Teil ein neues C# Script und nennt es "CameraController". Dieses Script könnt ihr nun gleich dem "Main Camera" GameObject zuweisen, wie das geht wurde euch bereits im ersten Teil der Tutorial Reihe gezeigt. Öffnet danach das Script um es editieren zu können. Ihr solltet nun genau dies hier sehen: using UnityEngine; using System.Collections; public class CameraController : MonoBehaviour { // Use this for initialization void Start () { } // Update is called once per frame void Update () { } } Wir wollen das dieses Script immer auf unser Zielobjekt schaut. Deswegen geben wr dem Script eine Variable die uns ermöglicht per Inspektor dieses Zielobjekt zu setzen. Fügt daher folgendes hinzu: public Transform Target; Transform bedeutet dass hier eine Transform Komponente erwartet wird, jedes GameObjekt hat diese Komponente und uns interessiert auch nur das was in dieser Transform Komponente zu finden ist (die Position). Wenn wir das Script nun speichern und nach Unity gehen können wir nun im Inspektor des ausgewählten "Main Camera" GameObjectes unser Camera Controller sehen und dort auch ein Platz in der wir unser "Target" angeben können. Klickt auf dieses Feld und wählt im darauf folgenden Dialog unser "Spieler Raumschiff" aus. Wenn wir nun auf Play drücken passiert noch rein gar nichts, dies werden wir aber schnell ändern. Geht dazu wieder in das "CameraController" Script und ändert void Update zu void LateUpdate. LateUpdate wird so wie Update jeden Frame aufgerufen. Der Unterschied ist aber dass LateUpdate aufgerufen wird nachdem sowohl die Physik Berechnungen als auch alle anderen Updates durchgeführt wurden. Der Ideale Zeitpunkt also um unsere Kamera zu positionieren ohne dass es zu hässlichen Ruckeleffekten kommt. Fügt nun in diese Methode folgendes hinzu: if (!Target) { return; } !Target bedeutet dass wenn wir kein Target ausgewählt wurden der bool Wert true generiert wird. Einfach nur Target ohne das ! Symbol würde bedeuten dass true nur dann generiert wird wenn Target einen Wert hat. Wir wollen an dieser Stelle aber die Ausführung der LateUpdate Methode beenden wenn kein Target gewählt wurde, weswegen wir auf die Nicht Existenz des Targets prüfen und mit return die Methode wieder verlassen sollte Target nicht gesetzt sein. Dies verhindert Fehler wenn wir mal in die Situation kommen in der unser Spieler Raumschiff verschwindet, weil es zb zerstört wurde. Um die Kamera immer mit einer gewissen Distanz von unserem Target zu halten, fügen wir eine weitere public Variable ein: public float Distance = 10.0f; Hierzu gibt es nicht viel zu sage, da die Verwendung das Interessante ist. Dazu erweitern wir die LateUpdate Methode ein weiteres mal, fügt unter die if Bedingung folgendes hinzu: transform.position = Target.position - Distance * transform.forward; Der Code berechnet eine Position der Camera relativ zum Target. Wir benutzen dazu die Position des Targets (Target.position) und verschieben diesen Wert mithilfe der Blickrichtung unserer Camera (transform.forward). Indem wir unsere Distance Variable mit der Blickrichtung multiplizieren setzen wir die Entfernung fest in der sich die neue Position zum Target befinden soll. Mit dem = weisen wir die neu berechnete Position unserer Camera zu (transform.position). Wenn wir nun das Script speichern sollte es so aussehen: using UnityEngine; using System.Collections; public class CameraController : MonoBehaviour { public Transform Target; public float Distance = 10.0f; void Start() { } void LateUpdate() { if (!Target) { return; } transform.position = Target.position - Distance * transform.forward; } } Wenn wir nun unsere Scene in Unity starten und unser Raumschiff bewegen wollen, sehen wir.. dass sich unser Raumschiff nur dreht aber nicht bewegt! Dies liegt daran dass uns Anhaltspunkte zu dieser Bewegung fehlen, da die Camera ja nun immer auf unser Raumschiff schaut. Um einen Anhaltspunkt zu erschaffen fügen wir ein neues GameObjekt in die Szene ein. Wir benutzen dafür den Menüpunkt: "GameObject"->"Create Other"->"Sphere" Wählt die "Sphere" in Hierarchy Bereich aus und wählt eine Position im Inspektor aus welche in der Nähe unseres Raumschiffes liegt, dieses aber nicht überlappt. Wählt zB die Position: X = 0, Y = 0 und Z = 3. Wenn wir nun die Scene erneut starten können wir sehen dass sich unser Raumschiff relativ zu dieser "Sphere" bewegt. Die Camera zeigt dabei immer fleißig auf unser Raumschiff. Hurra! Im nächsten Teil der Reihe lösen wir den Code des PlayerControllers etwas auf und erstellen uns ein Script für den Antrieb des Schiffes. Feedback ist gern gesehen. Teil 3: http://forum.unity-community.de/topic/7102-wir-bauen-uns-ein-space-shoot-em-up-teil-3-antrieb/
  5. 8 likes
    Zeit für etwas Umbauarbeiten, bisher haben wir einen PlayerController der etwas mehr macht als nur zu kontrollieren. Schwer zu glauben bei nur 43 Zeilen nicht wahr? Schauen wir uns einmal an was unser PlayerController so macht, er reagiert auf Tastendrücke vom Spieler und verändert direkt dadurch die Geschwindigkeit und Richtung unseres Raumschiffes. Das sind 2 verschiedene Aufgabenbereiche die wir trennen sollten. Um anschließend die Stärke des von Unity uns auferlegten Komponentensystemes auszuspielen. Wiederverwendbarkeit! Unser PlayerController Script sollte wie der Name schon impliziert nur dafür zuständig sein die Eingaben des Spielers auszuwerten und das was dann damit gemacht werden sollte an andere Komponenten übergeben. Deswegen die Zweiteilung des Scriptes. Ein Teil wird die Eingaben entgegen nehmen, dies bleibt unser PlayerController und der andere Teil wird die Geschwindigkeit und Drehung des Schiffs kontrollieren. Wir fangen an indem wir uns ein neues C# Script erstellen und es "Drive" (Antrieb) nennen. Für unsern Antrieb, welchen wir später wiederverwenden werden (zB für gegnerische Raumschiffe) benötigen wir folgende Dinge: - Beschleunigen (Accelerate) - Abbremsen (Deaccelerate) - Drehen Und um die Steuerung leichter zu machen fügen wir noch etwas hinzu: - Automatisches abbremsen Automatisches abbremsen ist eine passive Eigenschaft unseres Scriptes, bedeutet das wir es nicht extra aufrufen müssen damit es funktioniert. Wir beginnen daher damit dass wir uns einige Basiselemente vom PlayerController nehmen und diese im Drive Script einfügen. Und zwar alle Elemente die direkt den Antrieb des Schiffes betreffen. Was so ziemlich alle Variablen des PlayerController Scriptes wären: public float Acceleration = 1000.0f; public float Deacceleration = 1000.0f; public float RotationAcceleration = 300.0f; public float MaxForce = 1000.0f; public float MinForce = -500.0f; public float CurrentForce = 0.0f; Dies sollte im Drive Script stehen und nicht im PlayerController Script. Nun legen wir mit der ersten Fähigkeit unseres Drive Scriptes los: Beschleunigen! Erstellt eine neue Methode mit dem Namen Accelerate welche einen zusätzlichen float Parameter entgegen nimmt. Dieser Parameter soll dazu dienen fein zu regeln wie sehr wir beschleunigen wollen. Da wir bisher selbst noch keinerlei Parameter in selbst geschriebenen Methoden benutzt haben, hier die Methode: public void Accelerate(float factor) { } Wir benutzen hier so wie bei den Variablen ein public direkt vor der Methode. Da wir diese Methode von dem PlayerController Script aufrufen wollen muss diese public sein (öffentlich erreichbar). Wer nun ein wenig aufgepasst hat weiß auch was ungefähr in diese Methode hinein muss. Den Part vom PlayerController Script welcher ausgeführt wird wenn wir die nach Oben Taste drücken: CurrentForce += Time.deltaTime * Acceleration; Wir müssen noch unseren factor verwenden, wir verwenden ihn indem wir alles rechts von += mit dem factor multiplizieren. CurrentForce += Time.deltaTime * Acceleration * factor; Genau das Gleiche erledigen wir für die Deaccelerate Methode: public void Deaccelerate(float factor) { CurrentForce -= Time.deltaTime * Deacceleration * factor; } Wenn ihr euch noch daran erinnert haben wir im PlayerController Script eine Begrenzung der CurrentForce eingebaut, bisher machen wir dies immer egal ob wir Beschleunigt haben oder Abgebremst haben. Diesen kleinen Fehler können wir beheben indem wir die Stelle welche die Begrenzung aufrechterhält, nach Accelerate und Deaccelerate überträgt. Als Beispiel, so sollte die Accelerate Methode nach dieser Ergänzung aussehen: public void Accelerate(float factor) { CurrentForce += Time.deltaTime * Acceleration * factor; CurrentForce = Mathf.Clamp(CurrentForce, MinForce, MaxForce); } Nun haben wir die Beschleunigung und Abbremsung, aber etwas Kleines fehlt hierbei noch, wir wollen ja eine automatische Abbremsung wenn weder beschleunigt als auch abgebremst wurde. Aus diesem Grund merken wir uns wenn eines dieser beiden Aktionen ausgeführt wurde. Dazu erstellen wir uns zuerst eine neue Variable für das Drive Script: private bool hasChangedCurrentForce = false; Wir ihr bemerkt habt ist diesmal ein private vor dem Typ und kein public. Dies liegt daran dass wir nicht wollen dass diese Variable ausserhalb des Scriptes Bedeutung hat und auch nicht angefasst werden soll. private bedeutet dass die Variable oder Methode nur im inneren der Klasse in der es definiert wurde verwendet werden kann. Wir setzen hasChangedCurrentForce auf true immer dann wenn wir Accelerate oder Deaccelerate verwenden: hasChangedCurrentForce = true; Fügt diese Zeile in beide Methoden ein. Nun fehlt nur noch das Drehen, dies können wir lösen indem wir uns 2 Methoden, für jede Richtung in die wir drehen können wollen eine, einfügen. Wie bei Accelerate und Deaccelerate benutzen wir einen factor Parameter. Und wie zuvor übernehmen wir zu aller erst den Inhalt aus dem PlayerController Script welche für die Drehungen zuständig waren: public void RotateRight(float factor) { rigidbody.AddTorque(0, Time.deltaTime * RotationAcceleration * factor, 0); } public void RotateLeft(float factor) { rigidbody.AddTorque(0, -Time.deltaTime * RotationAcceleration * factor, 0); } Das wars. Die Drehung wurde eingebaut und sollte funktionieren. Was nun noch fehlt ist die automatische Abbremsung und die Anwendung der CurrentForce. Der letzte Part ist der einfachste, da wir auch hier direkt den Code aus dem PlayerController übernehmen können. Daher kopiert die notwendige Zeile und fügt sie in die Update Methode des Drive Scriptes ein: void Update () { rigidbody.AddForce(CurrentForce * Time.deltaTime * transform.forward); } Nun zum letzten Feature des Scriptes, die automatische Abbremsung. Da wir am Anfang des Tutorials klar gestellt haben wann dies einsetzen soll und zwar nur dann wenn weder beschleunigt noch abgebremst wurde, können wir bereits die ersten Zeilen dafür schreiben (wieder in die Update Methode): if (!hasChangedCurrentForce) { } Wir setzen hasChangedCurrentForce immer auf true wenn wir beschleunigen oder abbremsen. Nach der Prüfung sollten wir daher die Variable wieder auf false setzen. Schreibt dies direkt unter den if Scope: hasChangedCurrentForce = false; Wie soll nun aber die automatische Abbremsung funktionieren? Ganz einfach, wir schauen uns die Richtung an in der unser Raumschiff schaut (transform.forward) und vergleichen dies mit der Richtung und Geschwindigkeit in der wir uns bewegen (rigidbody.velocity). Wir benutzen dazu die Vector3.Dot Methode welche uns einen Wert liefert den wir benutzen werden um zu entscheiden ob wir Beschleunigen müssen (wenn wir rückwärts fliegen) oder Abbremsen müssen (wenn wir vorwärts fliegen). Tragt dazu folgendes in den if Scope für die automatische Abbremsung ein: var forwardFactor = Vector3.Dot(transform.forward, rigidbody.velocity); Diesen forwardFactor verwenden wir nun für die Entscheidung was gemacht werden soll: if (forwardFactor > 1.0f) { Deaccelerate(1.0f); } else if (forwardFactor < -1.0f) { Accelerate(1.0f); } else { CurrentForce = 0; } Das if Keyword kennen wir ja bereits, neu sind dagegen das else if und das else. else if wird aufgerifen wenn das vorherige if oder ein vorheriges else if selbst nicht erfüllt werden konnten. else if bietet also einen alternativen Weg an. Sollten auch alle folgenden else if scheitern so wird der Code im else Scope ausgeführt. if, else if und else können also übersetzt werden mit: Wenn X dann Y, Ansonsten Wenn Z dann W, Ansonsten R. Um einen Ping Pong Effekt zu vermeiden interessieren wir uns nur forwardFactor wenn er größer 1 oder kleiner -1 ist, im anderen Fall lassen wir den Luftwiederstand für uns arbeiten, den es im Weltraum zwar nicht gibt, aber für das Spielgefühl dennoch zuträglich ist. Wir stellen daher den "Drag" Wert in der "Rigidbody" Komponente des Spieler Raumschiffs auf 1. Dies sorgt dafür dass wir wenn wir keine Kraft auf unser GameObject auswirken, dieses GameObjekt immer langsamer wird. Unser Drive Script ist damit fertig. Zur Vollständigkeit halber, hier das Drive Script: using UnityEngine; using System.Collections; public class Drive : MonoBehaviour { public float Acceleration = 1000.0f; public float Deacceleration = 1000.0f; public float RotationAcceleration = 300.0f; public float MaxForce = 1000.0f; public float MinForce = -500.0f; public float CurrentForce = 0.0f; private bool hasChangedCurrentForce = false; // Use this for initialization void Start () { } // Update is called once per frame void Update () { rigidbody.AddForce(CurrentForce * Time.deltaTime * transform.forward); if (!hasChangedCurrentForce) { var forwardFactor = Vector3.Dot(transform.forward, rigidbody.velocity); if (forwardFactor > 1.0f) { Deaccelerate(1.0f); } else if (forwardFactor < -1.0f) { Accelerate(1.0f); } else { CurrentForce = 0; } } hasChangedCurrentForce = false; } public void Accelerate(float factor) { CurrentForce += Time.deltaTime * Acceleration * factor; CurrentForce = Mathf.Clamp(CurrentForce, MinForce, MaxForce); hasChangedCurrentForce = true; } public void Deaccelerate(float factor) { CurrentForce -= Time.deltaTime * Deacceleration * factor; CurrentForce = Mathf.Clamp(CurrentForce, MinForce, MaxForce); hasChangedCurrentForce = true; } public void RotateRight(float factor) { rigidbody.AddTorque(0, Time.deltaTime * RotationAcceleration * factor, 0); } public void RotateLeft(float factor) { rigidbody.AddTorque(0, -Time.deltaTime * RotationAcceleration * factor, 0); } } Nun müssen wir es nur noch benutzen. Auf auf zum PlayerController Script, welches nun so aussehen sollte: using UnityEngine; using System.Collections; public class PlayerController : MonoBehaviour { // Use this for initialization void Start () { } // Update is called once per frame void Update () { if (Input.GetKey(KeyCode.UpArrow)) { } if (Input.GetKey(KeyCode.DownArrow)) { } if (Input.GetKey(KeyCode.RightArrow)) { } if (Input.GetKey(KeyCode.LeftArrow)) { } } } Dieses Script wird unser Drive Script benutzen, daher sollten wir uns dieses Drive Script auch suchen. Fügt daher eine neue Variable in das PlayerController Script hinzu: private Drive drive; private deswegen weil es nur für die Internas des PlayerController Scriptes von Relevanz ist. Wir weisen diese Variable innerhalb der Start Methode zu: drive = GetComponent<Drive>(); GetComponent sucht eine Komponente, welche Komponente geben wir über einen generischen Parameter an. Die spitzen Klammern (< und >) umgrenzen die generischen Parameter. Generische Parameter sind immer Typen, mit GetComponent<Drive>() sagen wir demnach dass wir eine Komponente an unserem GameObjekt suchen welche vom Drive Typ ist. Alternativen zu dieser Variante eine Komponente zu suchen gibt es ebenfalls, entweder per Namen der Komponente (GetComponent("Drive")) oder wieder über den Typen der Komponente (GetComponent(typeof(Drive))). Wir benutzen die generische Variante, weil es automatisch einen Fehler gibt wenn es den Typen nicht gibt (zB: ein anderen Namen hat) was bei GetComponent("Drive") keinen Fehler geben würde. Ausserdem hat sie den Vorteil besser lesbar zu sein als GetComponent(typeof(Drive)). Ausserdem bekommen wir direkt als Rückgabetyp unser Drive zurück. Bei allen anderen Varianten müssen wir anschließend den Rückgabewert erst umwandeln, was wieder mehr Schreibarbeit bedeutet und auch unlesbarer wäre, vergleicht selber: drive = GetComponent<Drive>(); vs: drive = (Drive)GetComponent(typeof(Drive)); Fügt nun die Verwendung unseres Drive Scriptes in die Update Methode ein: void Update () { if (drive) { if (Input.GetKey(KeyCode.UpArrow)) { drive.Accelerate(1.0f); } if (Input.GetKey(KeyCode.DownArrow)) { drive.Deaccelerate(1.0f); } if (Input.GetKey(KeyCode.RightArrow)) { drive.RotateRight(1.0f); } if (Input.GetKey(KeyCode.LeftArrow)) { drive.RotateLeft(1.0f); } } } Wie man vermutlich gut sehen kann haben wir eine weitere if Bedingung eingefügt: if (drive) Damit können wir testen ob unsere Drive Komponente eventuell nicht gefunden oder gar gelöscht wurde. Das fertige PlayerController Script sollte nun so aussehen: using UnityEngine; using System.Collections; public class PlayerController : MonoBehaviour { private Drive drive; // Use this for initialization void Start () { drive = GetComponent<Drive>(); } // Update is called once per frame void Update () { if (drive) { if (Input.GetKey(KeyCode.UpArrow)) { drive.Accelerate(1.0f); } if (Input.GetKey(KeyCode.DownArrow)) { drive.Deaccelerate(1.0f); } if (Input.GetKey(KeyCode.RightArrow)) { drive.RotateRight(1.0f); } if (Input.GetKey(KeyCode.LeftArrow)) { drive.RotateLeft(1.0f); } } } } Startet nun wieder die Szene in Unity und schaut wie sich das ganze nun verhält. Abseits vom automatischen Abbremsen sollte sich alles wie gehabt verhalten. Feedback ist wiederum gern gesehen. PS: Das Forum macht mir immer die Tabs im Code kaputt ;( Teil 4: http://forum.unity-community.de/topic/7112-wir-bauen-uns-ein-space-shoot-em-up-teil-4-waffen/
  6. 6 likes
    Aktuell mein Projekt. Soll kleines Archer vs Archer vs Archer sein und zwar online. Habe sonst kein Ziel damit. Soll einfach just 4 fun sein.
  7. 4 likes
  8. 4 likes
  9. 4 likes
    ...ich mache mal ne Blutgrätsche: Hat das noch mit "handwerklichem Können" zu tun, sich von Tools, komplett alles an Arbeit abnehmen zu lassen und nur noch Regler hin und her zu schieben? Character von der Stange? Visuelle Programmierung? Animationen kaufen? ...das sind alles Fakten, die alle Assets beliebig machen und wenig Wiedererkennungswert bringen, da man sich nur in einem fest abgesteckten Rahmen bewegen kann. Ich persönlich lege viel Wert auf das handwerkliche Können, diese Dinge von Grund auf selber zu erstellen. Das macht nicht abhängig von Dritten. Das beweißt, dass man sich damit auseinandergesetzt hat...Lernkurven! Wissen! Erfahrung! Klar greifen wir alle immer mal auf irgendwelche Tools zurück... aber in dem Maß?! Nunja...muss ja auch jeder für sich entscheiden, ABER daran lassen sich Dinge messen. Bin ich in der Lage den Baukasten nur zu benutzen oder Bin auch in der Lage ihn vorher zu bauen? ...zumindest theroretisch um zumindest Anpassungen vornehmen zu können. Klar, als Hobbyprojekt geht das durch... kein Thema. Und sicher kann man Vieles davon auch als Basis für professionelle Projekte nutzen... Für mich ist das allerdings wie der Unterschied zwischen McDonalds und selber kochen. Es sieht zwar Alles ganz nett aus, aber unter der Haube... Ich finde es sogar wesentlich besser einen Stil zu kopieren, von HAND nachzuarmen (Lerneffekt!!!) und zu modifizieren, als Regler zu schieben, bis man einen Character hat, der aussieht wie 3 Trilliarden andere. Nimm die Kritik nicht persönlich - das ist eben so eine Grundsatzdebatte. Ein Grund warum gefragt wird, wer die Sachen wie gemacht hat. Viel Spass beim weiterbasteln!
  10. 3 likes
    Hallo Gamedevs und Unity-Fans. Heute habe ich die Grenze von 9000 subscribern für meinen Youtube-Kanal erreicht. Als Dankeschön sage ich Euch in diesem Video wo Ihr meine Low Poly model assets kostenlos von Patreon runterladen könnt: Viel Spaß!
  11. 3 likes
    Und ich bastele gerade weiter an einem Schwertnahkampf mit Schild in einer Testszene. Der Ritter hat sich gerade einen Treffer eingefangen.
  12. 3 likes
    Khetanna heißt das Werk der letzten 3 tage :). Jabbas persöhnliche luxus Jacht. Details wie bewaffnung werde ich später nochmal extra machen :).
  13. 3 likes
  14. 3 likes
    Hallo liebe Unity Insider Community, ich habe die Seite grade erst beim suchen nach Unity Tutorials gefunden und bin begeistert ! Dann habe ich den Vorstellungsbereich hier gefunden und war noch mehr erfreut: Richtig schöne Indie-Spiele-Perlen gibt es hier, genug um eine schöne Beschäftigung für das nächste verregnete Wochenende zu haben Was mich auch direkt zu meiner Projektvorstellung bringt: Ich habe grade erst ein Webprojekt veröffentlicht bei dem es darum geht das Hobby-/Indiespieleprogrammierer ihre Spieleprojekte dort vorstellen können, egal ob diese schon abgeschlossen sind oder sich noch in einer früheren Entwicklungsphase befinden. Das Ziel, bzw. mein Wunschtraum ist es das so irgendwann eine schöne Übersicht über die deutsche Hobby-/Indiespieleprogrammierer Szene entsteht und woran grade so gearbeitet wird. Die Entwickler bekommen eine weitere Möglichkeit ihr Spiel vorzustellen und Feedback einzusammeln und interessierte Spieler können so leicht und strukturiert nach frischer Indie-Spielekost aus dem deutschsprachigen Raum suchen. Die Seite läuft erst seit kurzem und sie befindet sich noch im Beta-Stadium, also habt bitte etwas Nachsicht wenn noch Fehler auftreten. Aber ich arbeite fleißig daran sie durch regelmäßige Updates zu verbessern und zu erweitern. Ein wenig zur Technik: Die Seite wird mit dem üblichen JavaEE Stack entwickelt: JPA, EJB, CDI, JSF, JAAS, JCA Fürs Logging nutze ich Logback mit der SLF4j API und als komponentenbibliothek nutze ich Primefaces. PrettyFaces für hübsche URLs und Caching mit EHCache. Rome zum erstellen von RSS/Atom Feeds und Atmosphere für Websockets mit Fallback. Das ist zwar mehr arbeit als einfach ein fertiges CMS zu nutzen, gibt aber deutlich mehr Flexibilität, denn ich möchte auf lange Sicht gesehen so etwas wie indiedb/gamejolt/moddb für den deutschsprachigen Raum machen. Und da aller Anfang schwer und die Seite grade erst am entstehen ist: Falls ich ein wenig Interesse wecken konnte und jemand mal reinschauen oder gar sein Projekt eintragen möchte: Ich würde mich sehr darüber freuen. Auch jegliche Kritik ist sehr willkommen. Ich hoffe es ist in Ordnung die Seite hier vorzustellen. Falls das als ungewollte Werbung gilt bitte ich vielmals um Entschuldigung und bitte darum diesen Thread einfach zu löschen. Die Adresse lautet: http://pewn.de grüße, krazun
  15. 2 likes
    Normal solltest du mit der Animator-Komponente arbeiten und nicht mit "Animation": Wenn du die Animation auf das entsprechende Objekt ziehst, dann sollte Unity einen Animator erstellen und im Animator sollte deine Animation als Zustand zu sehen sein. Hier ein Beispiel, wo er deine Animation abspielt, wenn du Space drückst. Animator anim; void Start () { anim = GetComponent<Animator>(); } void Update () { if(Input.GetKeyDown(KeyCode.Space)) { if (null != anim) { Debug.Log("Playing 'Sprungbrett Animation'"); anim.Play("Sprungbrett Animation"); } } }
  16. 2 likes
    Ein Escape Pod, besser gesagt der von C3PO und R2D2 https://sketchfab.com/models/744b53ababd640c089b9db03b02c968d
  17. 2 likes
    In dem Projekt steckt zweifellos schon viel Arbeit drin. Es ist super interessant was ihr da für eine Welt aufgebaut habt, aber ich muss ein bisschen Kritik zur Farbgestaltung los werden. Die Landschaft ist sehr monoton, grau-braun und düster gehalten. Das steht im absoluten Kontrast zu den knallbunten Neonfarben der Kreaturen, Effekte und Beleuchtungen. Dazu scheinen die Farben kein bestimmtes Thema zu haben und tauchen in willkürlichen Kombinationen auf. Das macht es schwer Formen zu erkennen und zu unterscheiden. Beim Charakterdesign (und nicht nur da) ist es wichtig passende Farben zu finden, die miteinander harmonieren. Es gibt ein paar einfache Regeln die, einem das Designen erleichtern. Eine davon ist; nie mehr als drei Farbgruppen pro Figur. Farbkreis und Grundfarben Der Farbkreis ist immer gleich aufgebaut (manchmal gespiegelt oder gedreht, ...). Die drei Grundfarben rot, blau, gelb kann man ohne bedenken kombinieren. Wenn man das Dreieck im Kreis dreht, würden alle Farben an seinen Spitzen zueinander passen. Analoge Farben: Wirf einen Blick auf den Kreis und such dir die bevorzugte Farbe. Die Farben links und rechts davon sind "Analoge Farben" und passen automatisch dazu und ergibt immer ein harmonisches Bild. Da kann man nix falsch machen. Komplementär Farben: sind die Farben gegenüber der gewählten Hauptfarbe. Sie bilden einen starken Kontrast, aber ergänzen sich trotzdem perfekt. Hoffe das hilft euch weiter. Auf das wir bald Neues von euch sehen.
  18. 2 likes
    Er ist fertig! Ich habe den letzten Monat viel in Grafik, Animation und Sound investiert und natürlich Bugs entfernt. Nun ist es aber geschafft. Auf dem Tisch können bis zu 7 Bälle gleichzeitig gespielt werden. Es gibt 4 Möglichkeiten für Multibälle von denen 2 recht leicht und 2 recht schwer zu erreichen sind. Da sich die Gegebenheiten auf dem Tisch auch ständig ändern, ist es nicht immer leicht. Der Tisch ist sowieso nicht leicht, auch wenn es den Anschein in meinem Video hat. Man hat bei dem Tisch aber eine hohe Lernkurve, denn man merkt ja gleich wenn man Mist gemacht hat! Wie auch beim letzten Tisch, habe ich wieder 3 Kamerapositionen eingebaut. Ja und ein Video, welches alle Gegebenheiten vom Flipper zeigt, habe ich natürlich auch: Viel Spaß beim anschauen und schreibt mir doch mal, was ihr von dem Tisch haltet.
  19. 2 likes
    Ende letzten Jahres hatte ich mit PAULS ADVENTURE angefangen mich in meiner Freizeit an einem kleinen JumpNRun zu üben. Im Rahmen der Entwicklung kam mir jedoch die Idee das ganze in eine neue Verpackung zu stecken und etwas "anderes" daraus zu machen. Zum Teil konnte ich ein paar Scripte aus dem alten Projekt übernehmen und für das neue "LANIAKEA" anpassen. Hoffe es gefällt dem Ein oder Anderen :-) Hier geht es zum Development-Blog Gruß Benni
  20. 2 likes
    Update: Ich möchte Euch nicht vorenthalten, daß man in Canyonbreed nun auch Begleiter spawnen kann, die entweder einfach nur gesammelt werden können, aber auch im Kampf ordentlich unterstützen. Der erste Begleiter, der auch im Clip zu sehen ist, bekommt man relativ früh über eine Quest. Leistungsfähigere Begleiter mit besseren Fähigkeiten können später vom Spieler gebaut werden. Das Handling der Begleiter wird noch weiter entwickelt. Es fehlt noch der "Lebensbalken" des Begleiters und auch die Kontrolle darüber, wie er auf seine Umgebung reagieren soll. Ich hoffe, Euch gefällt's.
  21. 2 likes
    Da fang ich mal an zu schreiben. Mal sehen ob jemand dazwischen Antwortet, denn es wird ein längerer Text. LowPoly kann man nicht an einer Anzahl an Polygonen festmachen, denn es ist, wie du ja schon selber ahnst, abhängig von der Hardware. Unity kann genauso viele Polys verkraften, wie alle anderen Engines auch. Man kann aber sagen, dass LowPoly so wenig wie möglich und so viel wie nötig bedeutet. Das Ganze hat einen historischen Hintergrund, der auch heute noch gilt, aber eben nicht mehr in so engen Grenzen liegt, wie damals. Wenn du z.B. eine Tasse modellieren würdest, dann könntest du diese Tasse richtig rund aussehen lassen oder eben sechs- oder achteckig. In den alten 3D Games waren die Tassen immer maximal achteckig. Das war z.B. auch noch bei HalfLife 1 so, obwohl da schon einige Jahre 3D-Grafik genutzt wurde. Die Grafikkarten hätten ohne weiteres eine Tasse mit 48 Rundungspolys rendern können. Aber das Problem war einfach die Anzahl der Polygone, die gleichzeitig zu sehen sind. Jedes Polygon muss berechnet werden und kostet etwas Zeit. Und irgendwann kommt man an die Grenze wo das Spiel nicht mehr flüssig zu spielen ist. Es ist ja nicht nur das Polygon, sondern sind auch die Shader, die Lichter, der Code und der Sound, die auch Zeit kosten. Aus diesem Grund hat man überall da Polygone reduziert, wo sie reduziert werden können. Denn man wusste wieviele Polys maximal sichtbar sein dürfen, damit sie auf einer gewissen Hardware noch ruckelfrei dargestellt werden können. Die Tassen wurden achteckig, weil sie eh nur Beiwerk sind und meist nur aus der Ferne zu sehen waren. Und selbst ein kamerafüllender Gegner, wie die Zombies, hatte auf dem PC nur rund 850 Polygone. Da ist klar, dass ein Arm maximal achteckig sein kann und viele Details verloren gehen. Klar man kann mit den Shadern und 'ner Normalmap schon einiges faken und es trotzdem recht gut aussehen lassen, aber die Texturgrößen war damals auch nicht berauschend, sodass alles unscharf wurde sobald man nah dran war. Bei Halflife 2 wurden schon viel mehr Polygone verbraucht, weil die Grafikkarten eben viel besser geworden sind. Hier waren es bei einem Menschen rund 6.000 Polygone. Also fast das Zehnfache zum Vorgänger und es sind nur 6 Jahre zwischen den zwei Spielen vergangen. Inzwischen haben die Hauptcharaktäre in den Spielen so rund 20.000 Polygone, hochauflösende Texturen und klasse Shader. Hier mal ein gutes Beispiel zu den Polygonzahlen von Lara Croft: http://kotaku.com/how-lara-croft-has-changed-over-the-last-16-years-1505856265 Diese recht hohe Polygonzahl findet man aber nur bei den Hauptcharaktären. Alles was Nebenwerk ist, ist auch in den Polys reduziert. Man braucht auch nicht noch mehr Polygone um etwas gut aussehen zu lassen. Jetzt mit 4k liegt man ungefähr bei 40.000 Polys für den Hauptchar. Aus dieser Sicht, würde ich sagen, dass ein LowPoly-Charakter heutzutage rund 2000-4000 Polygone hat. Eine Lowpoly-Kiste hat heute immer noch nur 12 Polygone (sechs Seiten mal 2 Dreiecke) denn man macht den Rest einfach über die Textur und den Shader. Die Shader können mittels Normalmap und Highmap dem Spieler vorgaukeln, dass eine flache Kiste doch erhaben ist und sogar Vertiefungen hat. Das funktioniert aber nur, wenn man gerade auf die Seiten guckt. Guckt man längs der Seite, so ist die Seite dann doch gerade wie ein Brett. Sollen echte Unebenheiten erkannt werden können, auch wenn man den Rand entlang schaut, dann muss das entweder ausmodelliert werden, was natürlich Polygone verbraucht, oder aber man nutzt DX11 und lässt die Grafikkarte wieder mittels eine Shaders echte Unebenheiten berechnen und mittels Tesselation darstellen. Es werden also zur Laufzeit Flächen ganz fein unterteilt und die neuen Eckpunkte räumlich verschoben. Kostet auch Zeit aber ist viel besser, als ein Modell mit einer Million Polys direkt in die Szene zu setzen. Brauchst du diese Unebenheiten in echt, musst du die auch ausmodellieren. Dein Modell ist dann aber immer noch Lowpoly, wenn du es nicht übertreibst. Lowpoly ist also das Minimum an Polygonen um gerade noch als Objekt zu fuktionieren und in der Kamera gut aussehen zu lassen. Wenn du bei einem PC Spiel versuchst, alle polys in der Szene bei maximal 30.000 zu lassen, dann wird das Spiel auf Grafikkarten laufen, die über 10 Jahre alt sind. (Wenn du es nicht mit dem Licht und den Shadern übertreibst)
  22. 2 likes
    Da ich immer versuche darauf zu achten, dass mein Spiel flüssig und gut läuft, versuche ich immer Performancetötende Funktionen zu meiden bzw. sie so selten wie möglich einzusetzen. Deswegen hatte ich einfach mal die Idee, alle gängigen Funktionen von denen man Oft gebrauch macht auf ihre Performance zu untersuchen. Das hier ist kein professioneller Test oder was auch immer. Ich möchte die jenigen, die vielleicht gerade mit Unity anfangen, eine kleine Übersicht geben welche Funktionen man doch besser lieber nicht jeden Frame aufrufen sollte. Excel-Tabelle: Rangliste 24.05.2017.xlsx Testablauf: Die zu testende Funktion wird 1.000.000 Mal pro Frame aufgerufen und die CPU-Zeit die dafür benötigt wird notiert. Dies würde bei der Funktion Vector3.Distance folgendem Script entsprechen: using UnityEngine; public class Test : MonoBehaviour { void Update () { Vector3 v1 = new Vector3(123, 456, 789); Vector3 v2 = new Vector3(987, 654, 321); for(int i = 0; i < 10000000; i++) { Vector3.Distance(v1, v2); } } } Im Stats Monitor lese ich dann die CPU-Zeit ab und trage sie in einer Tabelle ein. Mein PC: Grafikkarte: AMD Radeon 7850 Prozessor: AMD Phenom II 4x (3,4 GHz) RAM: 8 GB DDR2 So nachdem nun die Umstände geklärt sind, hier die Liste (Absteigend sortiert, beginnend mit der Performancelastigsten Methode): -------------------------------------------------------------------------------------------------------------------------------------------- GameObject.CreatePrimitive, GameObject.Instantiate, new GameObject, GameObject.Destroy... (>10.000 ms) Diese Funktion nimmt sehr viel Resourcen auf sich. Ist aber auch in Ordnung so, da das Erstellen von neuen GameObjects auch eigentlich nichts ist, was man hunderte Male pro Sekunde macht. Ich wollte es aber auf Grund der Vollständigkeit mit in diese Liste aufnehmen void Update () { for (int i = 0; i < 10000; i++) { GameObject.CreatePrimitive(PrimitiveType.Plane); } } (Getestet mit 10.000 Durchläufen Pro Frame: 960 ms. Hochgerechnet auf 1.000.000: 96.000 ms) GameObject.FindGameObjectOfType / GameObject.FindGameObjectsOfType (> 10.000) bei einer Szene mit 200 GameObjects void Update () { for (int i = 0; i < 1000000; i++) { GameObject.FindObjectOfType(typeof(Transform)); } } GameObject.Find (6.800 ms) bei einer Szene mit 200 GameObjects void Update () { for (int i = 0; i < 1000000; i++) { GameObject.Find("SomePepper"); } } Vector3.SmoothDamp (810 ms) void Update () { Vector3 v1 = new Vector3(123, 456, 789); Vector3 v2 = new Vector3(987, 654, 321); Vector3 v3 = Vector3.zero; for (int i = 0; i < 1000000; i++) { Vector3.SmoothDamp(v1, v2, ref v3, 0.235F); } } Physics.SphereCastAll (650 ms) void Update () { Vector3 v1 = new Vector3(123, 456, 789); Vector3 v2 = new Vector3(123, 456, 789); for (int i = 0; i < 1000000; i++) { Physics.SphereCastAll(v1, 0.235F, v2); } } Physics.CapsuleCastAll (600 ms) void Update () { Vector3 v1 = new Vector3(123, 456, 789); Vector3 v2 = new Vector3(123, 456, 789); Vector3 v3 = new Vector3(1, 0, 0); for (int i = 0; i < 1000000; i++) { Physics.CapsuleCastAll(v1, v2, 0.235F, v3); } } Physics.BoxCastAll (580 ms) void Update () { Vector3 v1 = new Vector3(123, 456, 789); Vector3 v2 = new Vector3(123, 456, 789); Vector3 v3 = new Vector3(123, 456, 789); for (int i = 0; i < 1000000; i++) { Physics.BoxCastAll(v1, v2, v3); } } GameObject.FindGameObjectsWithTag (540 ms) bei einer Szene mit 200 GameObjects void Update () { for (int i = 0; i < 1000000; i++) { GameObject.FindGameObjectsWithTag("SomePepper"); } } Physics.OverlapCapsule (510 ms) void Update () { Vector3 v1 = new Vector3(123, 456, 789); Vector3 v2 = new Vector3(123, 456, 789); for (int i = 0; i < 1000000; i++) { Physics.OverlapCapsule(v1, v2, 0.235F); } } Vector3.Angle (480 ms) void Update () { Vector3 v1 = new Vector3(123, 456, 789); Vector3 v2 = new Vector3(987, 654, 321); for(int i = 0; i < 1000000; i++) { Vector3.Angle(v1, v2); } } Physics.OverlapBox(480 ms) void Update () { Vector3 v1 = new Vector3(123, 456, 789); Vector3 v2 = new Vector3(123, 456, 789); Quaternion q1 = new Quaternion(123, 456, 789, 1.0F); for (int i = 0; i < 1000000; i++) { Physics.OverlapBox(v1, v2, q1); } } Physics.RaycastAll (480 ms) void Update () { Vector3 v1 = new Vector3(123, 456, 789); Vector3 v2 = new Vector3(123, 456, 789); for (int i = 0; i < 1000000; i++) { Physics.RaycastAll(v1, v2); } } Physics.OverlapSphere (450 ms) void Update () { Vector3 v1 = new Vector3(123, 456, 789); for (int i = 0; i < 1000000; i++) { Physics.OverlapSphere(v1, 0.235F); } } Physics.CapsuleCast (420 ms) void Update () { Vector3 v1 = new Vector3(123, 456, 789); Vector3 v2 = new Vector3(123, 456, 789); Vector3 v3 = new Vector3(1, 0, 0); for (int i = 0; i < 1000000; i++) { Physics.CapsuleCast(v1, v2, 0.235F, v3); } } Physics.BoxCast (410 ms) void Update () { Vector3 v1 = new Vector3(123, 456, 789); Vector3 v2 = new Vector3(123, 456, 789); Vector3 v3 = new Vector3(123, 456, 789); for (int i = 0; i < 1000000; i++) { Physics.BoxCast(v1, v2, v3); } } Physics.SphereCast (380 ms) void Update () { Vector3 v1 = new Vector3(123, 456, 789); Vector3 v2 = new Vector3(123, 456, 789); RaycastHit result; for (int i = 0; i < 1000000; i++) { Physics.SphereCast(v1, 0.235F, v2, out result); } } Physics.SphereCastNonAlloc (350 ms) void Update () { Vector3 v1 = new Vector3(123, 456, 789); Vector3 v2 = new Vector3(1, 0, 0); RaycastHit[] result = new RaycastHit[4]; for (int i = 0; i < 1000000; i++) { Physics.SphereCastNonAlloc(v1, 0.235F, v2, result); } } Physics.CapsuleCastNonAlloc (320 ms) void Update () { Vector3 v1 = new Vector3(123, 456, 789); Vector3 v2 = new Vector3(123, 456, 789); Vector3 v3 = new Vector3(1, 0, 0); RaycastHit[] rh1 = new RaycastHit[4]; for (int i = 0; i < 1000000; i++) { Physics.CapsuleCastNonAlloc(v1, v2, 0.235F, v3, rh1); } } Physics.OverlapCapsuleNonAlloc (310 ms) void Update () { Vector3 v1 = new Vector3(123, 456, 789); Vector3 v2 = new Vector3(123, 456, 789); Collider[] results = new Collider[4]; for (int i = 0; i < 1000000; i++) { Physics.OverlapCapsuleNonAlloc(v1, v2, 0.235F, results); } } Vector3.MoveTowards (280 ms) void Update () { Vector3 v1 = new Vector3(123, 456, 789); Vector3 v2 = new Vector3(987, 654, 321); for (int i = 0; i < 1000000; i++) { Vector3.MoveTowards(v1, v2, 0.235F); } } Physics.BoxCastNonAlloc (280 ms) void Update () { Vector3 v1 = new Vector3(123, 456, 789); Vector3 v2 = new Vector3(123, 456, 789); Vector3 v3 = new Vector3(1, 0, 0); RaycastHit[] rh1 = new RaycastHit[4]; for (int i = 0; i < 1000000; i++) { Physics.BoxCastNonAlloc(v1, v2, v3, rh1); } } Vector3.ProjectOnPlance (280 ms) void Update () { Vector3 v1 = new Vector3(123, 456, 789); Vector3 v2 = new Vector3(987, 654, 321); for (int i = 0; i < 1000000; i++) { Vector3.ProjectOnPlane(v1, v2); } } Physics.CheckCapsule (270 ms) void Update () { Vector3 v1 = new Vector3(123, 456, 789); Vector3 v2 = new Vector3(123, 456, 789); for (int i = 0; i < 1000000; i++) { Physics.CheckCapsule(v1, v2, 0.235F); } } Vetcor3.ClampMagnitude (250 ms) void Update () { Vector3 v1 = new Vector3(123, 456, 789); for(int i = 0; i < 1000000; i++) { Vector3.ClampMagnitude(v1, 100); } } Physics.Raycast (240 ms) void Update () { Vector3 v1 = new Vector3(123, 456, 789); Vector3 v2 = new Vector3(123, 456, 789); for (int i = 0; i < 1000000; i++) { Physics.Raycast(v1, v2); } } GameObject.Find (240 ms) bei einer Szene mit 1 GameObject void Update () { for (int i = 0; i < 1000000; i++) { GameObject.Find("SomePepper"); } } Physics.OverlapBoxNonAlloc (230 ms) void Update () { Vector3 v1 = new Vector3(123, 456, 789); Vector3 v2 = new Vector3(123, 456, 789); Quaternion q1 = new Quaternion(123, 456, 789, 1.0F); Collider[] results = new Collider[4]; for (int i = 0; i < 1000000; i++) { Physics.OverlapBoxNonAlloc(v1, v2, results, q1); } } Physics.Linecast (230 ms) void Update () { Vector3 v1 = new Vector3(123, 456, 789); Vector3 v2 = new Vector3(123, 456, 789); for (int i = 0; i < 1000000; i++) { Physics.Linecast(v1, v2); } } Physics.RaycastNonAlloc (210 ms) void Update () { Vector3 v1 = new Vector3(123, 456, 789); Vector3 v2 = new Vector3(123, 456, 789); RaycastHit[] result = new RaycastHit[4]; for (int i = 0; i < 1000000; i++) { Physics.RaycastNonAlloc(v1, v2, result); } } Physics.CheckBox (210 ms) void Update () { Vector3 v1 = new Vector3(123, 456, 789); Vector3 v2 = new Vector3(123, 456, 789); Vector3 v3 = new Vector3(1, 0, 0); Quaternion q1 = new Quaternion(123, 456, 789, 1.0F); for (int i = 0; i < 1000000; i++) { Physics.CheckBox(v1, v2, q1); } } Vector3.Project (210 ms) void Update () { Vector3 v1 = new Vector3(123, 456, 789); Vector3 v2 = new Vector3(987, 654, 321); for (int i = 0; i < 1000000; i++) { Vector3.Project(v1, v2); } } GameObject.FindGameObjectWithTag (200 ms) bei einer Szene mit 200 GameObects void Update () { for (int i = 0; i < 1000000; i++) { GameObject.FindGameObjectWithTag("SomePepper"); } } Vector3.RotateTowards (200 ms) void Update () { Vector3 v1 = new Vector3(123, 456, 789); Vector3 v2 = new Vector3(987, 654, 321); for (int i = 0; i < 1000000; i++) { Vector3.RotateTowards(v1, v2, 0.235F, 0.356F); } } Physics.OverlapSphereNonAlloc (200 ms) void Update () { Vector3 v1 = new Vector3(123, 456, 789); Collider[] result = new Collider[4]; for (int i = 0; i < 1000000; i++) { Physics.OverlapSphereNonAlloc(v1, 0.235F, result); } } Physics.CheckSphere (180 ms) void Update () { Vector3 v1 = new Vector3(123, 456, 789); for (int i = 0; i < 1000000; i++) { Physics.CheckSphere(v1, 0.235F); } } Vector3.Slerp / Vector3.SlerpUnclamped (180 ms) void Update () { Vector3 v1 = new Vector3(123, 456, 789); Vector3 v2 = new Vector3(987, 654, 321); for (int i = 0; i < 1000000; i++) { Vector3.Slerp(v1, v2, 0.235F); } } Vector3.Normalize (150 ms) void Update () { Vector3 v1 = new Vector3(123, 456, 789); for (int i = 0; i < 1000000; i++) { Vector3.Normalize(v1); } } Vector3.Reflect (140 ms) void Update () { Vector3 v1 = new Vector3(123, 456, 789); Vector3 v2 = new Vector3(987, 654, 321); for (int i = 0; i < 1000000; i++) { Vector3.Reflect(v1, v2); } } Vector3.Max / Vector3.Min (130 ms) void Update () { Vector3 v1 = new Vector3(123, 456, 789); Vector3 v2 = new Vector3(987, 654, 321); for (int i = 0; i < 1000000; i++) { Vector3.Max(v1, v2); } } Vector3.Distance (90 ms) void Update () { Vector3 v1 = new Vector3(123, 456, 789); Vector3 v2 = new Vector3(987, 654, 321); for(int i = 0; i < 10000000; i++) { Vector3.Distance(v1, v2); } } Vector3.Lerp (90 ms) void Update () { Vector3 v1 = new Vector3(123, 456, 789); Vector3 v2 = new Vector3(987, 654, 321); for (int i = 0; i < 1000000; i++) { Vector3.Lerp(v1, v2, 0.235F); } } Vector3.Cross (60 ms) void Update () { Vector3 v1 = new Vector3(123, 456, 789); Vector3 v2 = new Vector3(987, 654, 321); for (int i = 0; i < 1000000; i++) { Vector3.Cross(v1, v2); } } Vector3.Scale (50 ms) void Update () { Vector3 v1 = new Vector3(123, 456, 789); Vector3 v2 = new Vector3(987, 654, 321); for (int i = 0; i < 1000000; i++) { Vector3.Scale(v1, v2); } } Vector3.Dot (40 ms) void Update () { Vector3 v1 = new Vector3(123, 456, 789); Vector3 v2 = new Vector3(987, 654, 321); for (int i = 0; i < 1000000; i++) { Vector3.Dot(v1, v2); } } Vector3.SqrMagnitude (30 ms) void Update () { Vector3 v1 = new Vector3(123, 456, 789); for (int i = 0; i < 1000000; i++) { Vector3.SqrMagnitude(v1); } } ------------------------------------------------------------------------------------------------------------------------------------------------------ WICHTIG!: Die CPU-Zeiten gelten ausschließlich für meine Testbedingungen. Die Angaben dienen nur als Vergleich (und sind nicht auf 10ms genau zu nehmen) damit (vor allem Anfänger) Performanceprobleme schneller finden und beheben können. Ich werde diese Liste noch erweitern wenn ich die Zeit dazu finde Rangliste 24.05.2017.xlsx
  23. 2 likes
    Ich vermute du kannst dein Problem lösen, indem du dein Objekt in ein leeres Gameobjekt verschiebst und du dein Objekt dann so drehst wie du es haben möchtest. Nach Außen hin hat das leere Gameobjekt dann die Achsen wie du sie benötigst und das Modell kannst du innen so hindrehen wie du es brauchst.
  24. 2 likes
    Nabend liebe Unity Community, ich wollte euch mal mein Spile vorstellen. "Farty The Owl". Denke der Name erklärt einiges Sinn des Spiels ist, mit einer mexikanischen furzenden Eule so hoch wie möglich zu kommen. Nichts spektakuläres, aber auch nichts Alltägliches. Leute die keine obszönen Pupsgeräusche mögen, sollten das Spiel eher meiden oder es ohne Ton spielen. Über Feedback und Support würde ich mich freuen. Schönen Restsonntag noch Thanatos https://play.google.com/store/apps/details?id=de.hydraware.fartytheowl&hl=de
  25. 2 likes
    Zeig es oben am besten als Grafik an, ich habe mittlerweile echt bedenken eine Excel- oder Worddatei anzuklicken, der Verschlüsselungstrojaner geht um und der verwendet eine Exceldatei + Makro.
  26. 2 likes
    Könnte eine (unerwünschte) Malware/Adware-Schutzprogramm namens "Web Companion LavasoftWeb" sein. Man erkennt sie an der DLL "LavasoftTcpService64.dll", die im Error.log auch als Absturzursache angeführt wird. Diese Malware kann zum Absturz von Unity beim Starten führen (was sie bei dir wohl auch tut). Bei dir liegt diese DLL unter "C:\Windows\system32\LavasoftTcpService64.dll". Vermutlich liegt diese Adware irgendwo unter "C:\Program Files\Lavasoft\Web Companion" oder "C:\Program Files (x86)\..." Hier steht es auch noch einmal beschrieben: http://answers.unity3d.com/page/troubleshooting.html Ich vermute die Malware lässt sich zum Beispiel mit der Software "AdwCleaner" entfernen. Bzw. stop ich lese gerade es sollte vor unerwünschter Adware schützen .. also es ist ein Adwareschutzprogramm das Unity stört...
  27. 2 likes
    Mhh hab die Ursache gefunden, liegt am "Render Mode" von den Point Lights. Stell den mal auf "Important". Wenn die nicht auf "Important" stehen fängt er wieder an das Licht über Vertex/Objekt zu interpolieren, das ist zwar schnell und kann gut aussehen, muss aber nicht. (zudem sahen die dunklen Schatten auf dem Gras auch seltsam aus, die sind nun auch weg)
  28. 2 likes
    Obwohl es interessant wäre, dem Problem auf den Grund zu gehen, kann man auf die Frage nur antworten: Das macht Sinn. Die Scripting-API ist zu 99.95% identisch und was du mit 5.0.0 lernst und baust wirst du in alle höheren 5.x-Versionen mitnehmen können. Gibt beim Upgrade gerade größerer Projekte immer mal wieder Probleme, aber nichts was sich nicht lösen ließe.
  29. 2 likes
    Poste mal dein Editorlog, das liegt unter ("xxx"="dein User" ... vermutlich "refre"): C:\Users\xxx\AppData\Local\Unity\Editor Das "error.log" und das "crash.dmp" kannst ja auch mal posten vielleicht sieht man was. (bitte als Dateien und nicht hier den Text reinposten) Hast du mal ein komplett neues Projekt erzeugt und dann Unity gestartet? Weil laut Log öffnest du da das "Standard Assets Example Projekt" (kann halt sein, daß das Projekt völlig veraltet ist und die neue Version damit nicht mehr klarkommt)
  30. 2 likes
    Die letzten Wochen waren hart und schwer gepflastert mit Herausforderungen, die ein Onlinespiel so mit sich bringen. Was in einer Offlineversion einfach umzusetzen ist, entpuppt sich später bei Onlinetests zu echten Hürden. Ich arbeite immer noch alleine an Canyonbreed und mache momentan nur kleine Schritte. Spaß macht es mir als Hobbyprojekt immer noch. Doch zurück zu Canyonbreed: Grundlegende Schwierigkeiten im Netcode habe ich mittlerweile in den Griff bekommen und kann schon bald damit beginnen, das Spiel mit Inhalten zu füttern. Die Offlinedemoversion funktioniert ja schon recht ordentlich. Ich arbeite aber schon seit Wochen auf eine Onlineversion hin, damit man auch mit mehreren Leuten und vor allem rund um die Uhr testen kann. Auch wenn ich wenig Resonanz auf mein Projekt bekomme, was sicher auch am mangelndem Marketing liegt, erhoffe ich mir, daß sich ein paar Leute finden werden, die dann mit dem Onlineclient spielen werden. Aussenbereich: Ich habe mich dazu entschlossen, die Story ein wenig zu ändern. Das Kolonialschiff "DSS CANYONBREED" ist nicht in einem Asteroidenfeld havariert, sondern in der Nähe eines Planeten. Dort wurde mittlerweile ein Aussenposten installiert. Dadurch eröffnet sich die Möglichkeit, im "Freien" zu spielen. Der Planet, der noch keinen festen Namen hat, ist so weit von seinem Stern entfernt, daß er so gut wie gar kein natürliches Licht empfängt. Hinzu kommt, daß sich der Aussenposten in einem Krater befindet. Lediglich künstliche Lichtquellen erhellen spärlich die Umgebung. Hier ein kurzer Einblick, ich hoffe es gefällt Euch:
  31. 2 likes
    Die Indexverletzung kommt aber von der oberen If-Abfrage und nicht von dem Klammerfehler weiter unten (diese Klammersetzung ist vermutlich auch falsch), da "i" hier zu weit hochgezählt wird. Ein kleines Beispiel: Felder = ("0","1","2"); Length ist damit 3 Für dem Fall i = 2 folgt if (GUI.Button(new Rect(10, 100, 100, 50), "vorwärts") && 2 < 3) => i++; => i = 3 => Felder[ i ].SetActive(true); => " Array index is out of range" Ich hatte auch noch eine Klammer zu wenig oben, habs korrigiert Btw. der Code aktiviert aktuell das Canvas an der 2. Position zuerst, wenn er von ganz vorn anfangen soll, dann "i" mit -1 initialisieren.
  32. 2 likes
    Im Endeffekt macht doch Unity alles für dich. Du musst natürlich dir selber ersteinmal überlegen, welche Texturgröße du auf einem Highend-Gerät haben willst. Gehst du also von einer HD Auflösung aus und gewisse Spielelemente sind Bildschirmfüllend zu sehen, so sollten die Texturen dieser Objekte auch solch eine Auflösung haben. Alles was nicht ganz nah zu sehen ist oder nicht so detailliert sein muss, machste in der Auflösung kleiner. Meistens macht es Sinn, ein Spiel für unterschiedliche Medien auch unterschiedlich aufzubauen. Bei den Texturen musst du aber nicht doppelte Dinge erstellen, denn je nach Build-Medium hast du andere Importeinstellungen. Wenn du also für IOS und Android (Also Smartphones oder Tabletts) die Importeinstellungen für die Textur auf Max 1024 einstellst, dann wird die Textur auch so eingeladen und beim Build genutzt. Unity verkleinert also die Textur, die vorher evtl. 2048x2048 war. Also überleg dir, wie groß eine Textur maximal sein muss und erstell sie dir so. Je nach Buildmedium stellst du dann für deine Texturen andere Importeinstellungen ein.
  33. 1 like
    Die Werte von Time.time sind jedesmal unterschiedlich so dass davon auszugehen ist dass die Charaktere zwischenzeitlich den Trigger wieder verlassen. Wie genau ist der Trigger denn aufgebaut? Leicht größer oder gleich groß wie der normale Collider von zB der Bodenplatte/dem Glas? Wenn ja wäre dies eine Erklärung, die Physikengine löst Kollisionen auf, dadurch kann es passieren dass allein durch die Korrektur damit 2 Objekte nicht ineinander stecken bleiben eine Trennung auftritt. Auch spielt es eine Rolle ob Objekte voneinander abprallen, selbst minimal könnte dies eine Trennung emittieren. Du könntest entweder die Trigger größer machen oder ein delay einbauen der verhindert dass zwei gleiche Objekte kurz folgend mit dem Glas kollidieren können.
  34. 1 like
    Du könntest ein Leeres Gameobjekt nutzen, welches nur einige Public Variablen in einem Script hat, wie z.B. deine ID und den Spielername. Dieses GO bekommt im Script dann in der Awake() oder Start() den Object Befehl DontDestroyOnLoad. Somit bleibt dieses GO bei jedem Szenenwechsel am Leben und kann jederzeit wieder genutzt werden. Natürlich musst du dieses GO jetzt manuell löschen, wenn du es nicht mehr brauchst oder wenn du z.B. in die allererste Szene zurück gehst. Du könntest auch eine Script nutzen, in dem nur einige Static Variablen zur Verfügung gestellt werden. Dieses Script braucht keinem Objekt zugewiesen werden. Static Variablen sind global gültig und können deswegen nur einmalig vorkommen. Sie behalten ihren Wert, bis das Spiel beendet wird oder sie eben mit neuen Werten bestückt werden. Und deswegen haut man sich schnell nen Bug ins Spiel, weil man gerne vergisst, die Variablen wieder zurück zu setzen. Die Variablen kannst du ganz leicht über scriptname.variablenname abfragen und beschreiben. Du musst nicht erst danach suchen und eine Referenz bilden. Static Variablen solltest du aber dennoch nur minimal nutzen. Für eine Datenablage, wie z.B. für ein SaveGame oder, wie in deinem Fall, für einzigartige Informationen sind sie gut. Zum Datenaustausch, weil es so schön einfach ist, solltest du sie nicht nutzen, denn du wirst dir ruck-zuck die Variablen zur falschen Zeit überschreiben und ewig nach der Fehlerquelle suchen.
  35. 1 like
    Deine Vorstellung klingt ganz richtig. Jede Komponente ist auch ein Objekt, genau wie jedes GameObject ein Objekt ist. Das, was du mit "eigenes Teleporterscript" meinst, ist also ein Objekt wie alles andere auch. Man könnte es auch "Instanz" der Scriptklasse nennen. Jedes Objekt hat nun seinen eigenen Zustand. Damit gemeint ist die Menge aller Werte, die das Objekt für die Variablen seiner Klasse hat. Bei der Light-Komponente ist die Eigenschaft "Color" eine dieser Variablen, und jede Light-Komponente hat dafür ihren eigenen Wert. Sonst würden ja alle Lichter mit derselben Farbe leuchten. Das Keyword "static" ist es, das diese Objektgebundenheit aufhebt. Statische Variablen gibt es pro Klasse nur einmal, und für alle Instanzen gilt derselbe Wert.
  36. 1 like
    Ja, der Animator hat einen Defaultzustand (das ist der orange Zustand) und den spielt er 1x ab, sobald der Animator aktiviert wird. Wenn du mehrere Animationen über den Animator verwalten möchtest, dann hast du 2 Möglichkeiten. Entweder du definierst Trigger in deinem Zustandsgraph, über die du dann die jeweilige Animation aufrufen kannst: int jumpHash = Animator.StringToHash("Jump"); Update() { // Zustandsübergang über entprechenden Trigger auslösen und damit die entsprechende Animation ausführen, auf die die Transition zeigt anim.SetTrigger (jumpHash); } oder - du stellst alle Animationen einzeln in den Zustandsgraph ein - stellst den orangen Zustand auf eine leere Animation (hier einfach "Leerlauf" genannt), indem du einen leeren Zustand erstellst und diesen als Default State markierst - und die Animationszustände dann wie oben aufrufst, in deinem Fall wenn der Trigger ausgelöst wird
  37. 1 like
    Ja das stimmt schon, habe nun einmal zusammen gezählt, wird schon einiges an Variablen werden, was ich pro Level brauche. Denke da werde ich schon in Richtung XML arbeiten müssen.
  38. 1 like
  39. 1 like
    Das mit dem Level kannst mit ScriptableObjects machen. Mir ist kein einfacher Weg bekannt alles zu speichern. Bei einer reinen Datenhaltungsklasse könnte man eine naive Serialisierung versuchen: https://stackoverflow.com/questions/36852213/how-to-serialize-and-save-a-gameobject-in-unity https://unity3d.com/learn/tutorials/topics/scripting/persistence-saving-and-loading-data
  40. 1 like
    Wenn ich ( in sehr seltenen Fällen ) Texturen erstelle, nehme ich eigentlich immer 2048 / 4096 und skaliere dann auf 1024 etc. runter. Das kommt natürlich auch etwas auf deinen Anwendungsfall an.. wenn es um ehr simplen Cartoonstyle geht oder Fotorealismus Aber wenn man anfängt alle Texturen in 512er Größe zu erstellen, ist es später sehr aufwendig, wenn man doch eine höhere Auflösung möchte. Mit hochauflösenden 'Basistexturen' lässt sich leichter noch was dran schrauben, wenn die Performance darunter leiden sollte.
  41. 1 like
    Genau so ist es. Kosmetische Dinge fange ich immer erst dann an, wenn der Prototyp solide funktioniert.
  42. 1 like
    Wow also die Idee mit dem Bgeleiter gefällt mir echt wahnsinnig gut. Bin in solchen Spielen sowieso immer ein Fan davon, wenn man nicht immer alles "alleine" durchsteht, sondern jemanden als KI dabei hat. Allerdings finde ich den Kampf zwischen dem Begleiter und den Gegnern noch recht unspektakulär. Aber ich denke mal, dass das sowiso noch Prototypenmäßig ist
  43. 1 like
    OnGui kann mehrmals pro Frame aufgerufen werden. Und bei jedem Aufruf wird der Scorewert für richtige Antworten um eins erhöht. Du solltest auch OnGui nicht mehr benutzen. Ist seit Unity 4.6(?) veraltet. Benutze die neue Unity-UI.
  44. 1 like
    danke - ach deshalb hat es sich abgeschaltet, stimmt da habe ich bei einer arrayabfrage indexoutofrange fabriziert, dann kann die Titanic ja erstmal weiterschippern, ahoi (bis zum nächsten Eisberg)
  45. 1 like
    Also bei mir funktioniert das Skript (auch wenn der Würfel irgendwo drin steckt). Du kannst ja noch zusätzlich alles außer deinem Würfel dem Layer "Ignore Raycast" zuweisen, dann kannst du deinen Würfel sogar durch die Wand hindurch verschieben. Wenn nämlich der "Ray" von deiner Mausposition durch einen anderes Objekt blockiert wird, dann wird OnMouseDrag() nicht ausgelöst.
  46. 1 like
    Du bekommst (oder hast bereits) ja einen orangen State (und einen Animator) wenn du die Animation auf dein Modell ziehst. Diesen State verdoppelst du einfach per Copy & Paste. Dann erzeugst du eine Transition vom orangen State zu deinem neuen grauen State. Und noch eine Transition vom grauen State zum orangen State wieder zurück. Wichtig ist nun noch, daß du beim grauen State den Speed auf einen negativen Wert stellst, damit läuft die Animation des grauen States rückwärts. Nun geh noch einmal auf deine Animation selbst und überprüfe, das diese nicht gelooped wird, d.h. den Haken bei "Loop Time" rausnehmen.
  47. 1 like
    Es gibt auch spezielle Shader, die einen Rahmen um das Mesh ziehen, das Problem hier bei ist nur, daß man dann quasi einen speziellen Shader dafür benötigt oder den Shader austauschen muss, sobald das Objekt markiert wird: http://wiki.unity3d.com/index.php/Silhouette-Outlined_Diffuse Hier noch eine bessere Variante die mit Rendertexture und Postprocessing arbeitet: https://willweissman.wordpress.com/tutorials/shaders/unity-shaderlab-object-outlines/
  48. 1 like
    Hallo, ich möchte mich mal kurz Vorstellen. Ich heiße Martin 29 komme aus Essen und möchte in die Welt der Spiele Entwicklung eintauchen. Ich habe früher viel Zeit in Joomla, Photoshop, Cinema 4D und und und gesteckt. Doch die Programmierung hat mich schon immer gereizt. Bis her hat mich allerdings immer meine Angst davon abgehalten, da zb. meine Englisch Kenntnisse nicht sonderlich gut sind. Mittlerweile denke ich mir egal. Einfach mal versuchen. Habe gestern die Unity 3D Version 5 installiert und mal ein wenig reingeschaut. Dazu habe ich mir direkt ein Tutorial geschnappt und muss sagen oha ist schon viel an Informationen die da auf einen Prasseln. Habt Ihr einen Tipp für mich wie ich die Sache am besten angehe? Habe mir eben noch ein Video Tutorial angeschaut bei dem ein "AddOn?" Namens Playmaker benutzt wird. Sollte ich sowas überhaupt nutzen? Oder lieber alles über C# Scripte selber schreiben? Nachtrag: Hier noch ein kleiner Einblick in mein Game Room (Hobby Nr 2 bzw doch eher Nr1) lg Martin Lenz
  49. 1 like
    Neue Unityversion? Wenn das Menuskript Fehler enthält, dann wird es von Haus aus überhaupt nicht ausgeführt, vermutlich hat dein Skripteditor das Skript geändert beim Beenden deiner "Arbeitssession" und evtl. damit Fehler übernommen. Vielleicht hat Unity automatisch eine Lightmap erstellt, dann diese einfach wieder löschen. Ansonsten mal den Libraryfolder deines Projektes löschen und dein Projekt neu öffnen, dann werden alle deine Assets neu geladen und der Assetcache von Unity wieder wieder neu erstellt.
  50. 1 like
    Noch bissel kamera shake mit vllt dotween & sound & Punktestand, dann public release