Du bist nicht angemeldet.

Stilllegung des Forums
Das Forum wurde am 05.06.2023 nach über 20 Jahren stillgelegt (weitere Informationen und ein kleiner Rückblick).
Registrierungen, Anmeldungen und Postings sind nicht mehr möglich. Öffentliche Inhalte sind weiterhin zugänglich.
Das Team von spieleprogrammierer.de bedankt sich bei der Community für die vielen schönen Jahre.
Wenn du eine deutschsprachige Spieleentwickler-Community suchst, schau doch mal im Discord und auf ZFX vorbei!

Werbeanzeige

RmbRT

Treue Seele

  • »RmbRT« ist der Autor dieses Themas

Beiträge: 169

Wohnort: Darmstadt

Beruf: Student

  • Private Nachricht senden

1

25.08.2014, 11:11

Designfrage zu einer Kameraklasse

Ich hoffe, dass das hier das richtige Unterforum für Designfragen ist.
Ihr kennt ja alle die verschiedenen Kamearmodi:
  • Die Ego-Kamera, die aus einer Blickrichtung und einem Fußpunkt besteht,
  • Die folgende Kamera, die aus einem Zielpunkt besteht, zu dem die Kameraposition relativ ist,
  • Die "lose" Kamera, bei der die Kameraposition und der Zielpunkt nicht voneinander abhängen (wie in Rennspielen, wenn die Kamera an einem Punkt bleibt, aber immer das Auto fokussiert).
Ich hatte mir überlegt, dass es umständlich wäre, die verschiedenen Modi zu benutzen, wenn ich nur einen der Modi implementiere (Oft ist es die dritte oder erste Variante, die von Kameraklassen verwendet wird). Ich dachte mir, dies mit einem Enum zu lösen:

C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/*Defines the behaviour of a Camera object.*/
enum CameraMode : ubyte_t
{
    /*Represents the Camera Position and Target as detached absolute coordinates.
    Pos contains the absolute coordinates of the Camera.
    Target contains the absolute coordinates of the view target.*/
    CAMERA_MODE_LOOKAT_FIX = 0x00,
    /*Represents the Camera Position as attached to the Target.
    Pos contains the coordinates of the Camera relative to the view target.
    Target contains the absolute coordinates of the view target.*/
    CAMERA_MODE_LOOKAT_REL = 0x0F,
    /*Represents the Camera Target as attached to the Camera.
    Pos contains the absolute coordinates of the Camera.
    Target contains the coordinates of the view target relative to the Camera.
    */
    CAMERA_MODE_FACEDIR = 0xF0
};

Die Kameraklasse sieht wie folgt aus:

C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
class Camera
{
public:
    /*Creates a Camera object.
    mode defaults to CAMERA_MODE_LOOKAT_FIX.*/
    Camera();
    Camera(CameraMode mode);

    /*Sets the Pos value.
    This is not necessarily the absolute coordinate of the Camera.
    Mind the current CameraMode configuration set.*/
    void setPos(const math::vec3 &pos);
    /*Sets the Target value.
    This is not necessarily the absolute coordinate of the view target.
    Mind the current CameraMode configuration set.*/
    void setTarget(const math::vec3 &target);

    /*Sets the CameraMode.
    Note that this overrides the field 'mode' without converting the values - for that purpose, use convertTo(CameraMode).*/
    void setMode(CameraMode mode);

    /*Returns the Pos field.
    Note that this is not necessarily the absolute Camera coordinate.
    Mind the current CameraMode configuration set.*/
    const math::vec3 &getPos();
    /*Returns the Target field.
    Note that this is not necessarily the absolute view target coordinate.
    Mind the current CameraMode configuration set.*/
    const math::vec3 &getTarget();

    /*Converts the internal value representation to the given model.*/
    void convertTo(CameraMode newMode);

    /*Returns the current camera mode.*/
    CameraMode getMode();

private:
    math::vec3 pos;
    math::vec3 target;
    CameraMode mode;
};

Wie schon zu erraten ist, ist jetzt Kamera != Kamera. Wenn ich eine Kamera von einer zur anderen Form konvertieren will, muss ich 'nen dicken Brocken Logik benutzen:

C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
void Camera::convertTo(CameraMode newMode)
{
    switch(mode)
    {
    case CAMERA_MODE_FACEDIR:
        {
            switch(newMode)
            {
            case CAMERA_MODE_FACEDIR:
                {
                    return;
                } break;
            case CAMERA_MODE_LOOKAT_FIX:
                {
                    target = pos+target;
                } break;
            case CAMERA_MODE_LOOKAT_REL:
                {
                    math::vec3 temp(target);
                    target = pos+target;
                    pos=-temp;
                } break;
            }
        } break;
    case CAMERA_MODE_LOOKAT_FIX:
        {
            switch(newMode)
            {
            case CAMERA_MODE_FACEDIR:
                {
                    target = target-pos;
                } break;
            case CAMERA_MODE_LOOKAT_FIX:
                {
                    return;
                } break;
            case CAMERA_MODE_LOOKAT_REL:
                {
                    pos = pos-target;
                } break;
            }
        } break;
    case CAMERA_MODE_LOOKAT_REL:
        {
            switch(newMode)
            {
            case CAMERA_MODE_FACEDIR:
                {
                    pos = target+pos;
                    target = target-pos;
                } break;
            case CAMERA_MODE_LOOKAT_FIX:
                {
                    pos = target + pos;
                } break;
            case CAMERA_MODE_LOOKAT_REL:
                {
                    return;
                } break;
            }
        } break;
    }
    mode = newMode;
}

Ist meine Realisierung des Problems guter/schlechter Stil? Würdet ihr anders an das Problem gehen?
(Hoffe, dass das Problem genau genug herüber kommt)

MfG,
RmbRT
"Dumm ist, wer dummes tut."

Schorsch

Supermoderator

Beiträge: 5 145

Wohnort: Wickede

Beruf: Softwareentwickler

  • Private Nachricht senden

2

25.08.2014, 12:20

Eigentlich ist das Verhalten einer Kamera eine Sache die du ganz gut auslagern kannst. Ich weiß nicht ob du dich schon mal mit Design Patterns auseinander gesetzt hast, aber guck dir doch mal das Strategy Pattern an. Das wäre meiner Meinung nach an dieser Stelle sinnvoll. Die Enumeration wirfst du raus und die Kamera bekommt ein Objekt welches die Logik der Kamera ausführen kann. Eine Kamera muss ja nicht nur beim wechsel des Modus Werte verändern, sondern verhält sich ach danach anders. Dieses Verhalten wird dann einfach ausgelagert.
„Es ist doch so. Zwei und zwei macht irgendwas, und vier und vier macht irgendwas. Leider nicht dasselbe, dann wär's leicht.
Das ist aber auch schon höhere Mathematik.“

3

25.08.2014, 13:00

Die Frage ist ein bisschen, wofür du eigentlich die Kameraklasse brauchst. Ist es beispielsweise sinnvoll, nur die Position und das Ziel zurück zu geben, oder würde nicht eigentlich eine View-Matrix reichen? Denn im Moment musst du die ja immer noch an einer anderen Stelle aus den entsprechenden Werten des Kameraobjektes zusammenbauen, und dadurch hast du die Kamera an zwei Stellen im Code behandelt.
Ich verstehe auch nicht, wieso du so einen dicken Brocken Code brauchst. Wieso ist es relevant, wie die Kamera vorher war? Eigentlich solltest du doch für jeden Kameratyp direkt die absoluten Werte liefern können.
Lieber dumm fragen, als dumm bleiben!

David Scherfgen

Administrator

Beiträge: 10 382

Wohnort: Hildesheim

Beruf: Wissenschaftlicher Mitarbeiter

  • Private Nachricht senden

4

25.08.2014, 13:12

Ich finde eine Kameraklasse schon sinnvoll. Da kann man z.B. Dinge wie das Wackeln der Kamera bei Explosionen einbauen.
Beim enum wundern mich vor allem die Werte. Warum werden die überhaupt per Hand festgelegt, und warum gerade auf die Werte 0, 15 und 240?

Legend

Alter Hase

Beiträge: 731

Beruf: Softwareentwickler

  • Private Nachricht senden

5

25.08.2014, 13:12

Ich benutze eine Kameraklasse, deren Hauptaufgabe ist es bei mir gerade die Viewmatrix zusammenzubauen.
Wenn ich so viele switches wie beim TE sehe, wird mir allerdings schon was übel.
In diesem Fall sehe ich da so: Du brauchst drei Kameraklassen, mit gemeinsamer Basisklasse.
"Wir müssen uns auf unsere Kernkompetenzen konzentrieren!" - "Juhu, wir machen eine Farm auf!"

Netzwerkbibliothek von mir, C#, LGPL: https://sourceforge.net/projects/statetransmitt/

Sacaldur

Community-Fossil

Beiträge: 2 301

Wohnort: Berlin

Beruf: FIAE

  • Private Nachricht senden

6

25.08.2014, 14:08

Ich denke, dass Schorsch da schon einen geeigneten Ansatz geliefert hat. Eventuell will man, dass die Kamera von einem Modus in einen anderen Wechselt, bspw. weil der Spieler in einen Abschnitt mit fester Kameraführung gelangt ist. Gibt es an der Kamera ein Objekt, welcher den Modus darstellt, spiegelt die Objektstruktur auch das wider, was man im Spiel erreichen will: eine Kamera mit verschiedenen Modi.
Und zu den von David angeschnittenen Dingen gehören auch noch viel grundlegendere Sachen, wie eine Kamera, die einem anderen Objekt in der Szene folgt, Zoom/Enfernung zu diesem Objekt usw. (Würde man ohne Kamera-Klasse arbeiten, könnte man das zwar ebenfalls umsetzen, müsste dies aber in andere Klassen reinmischen.)
Spieleentwickler in Berlin? (Thema in diesem Forum)
---
Es ist ja keine Schande etwas falsch zu machen, als Programmierer tu ich das täglich, [...].

7

25.08.2014, 14:55

Imho sollte eine Kameraklasse Funktionen vorgeben zur Matrixberechnung, Viewfrustum berechnung, Viewfrustum-culling und weiteren nützlichen Sachen, die die Kamera selbst tun kann.
Stelle ich mir eine Spiegelreflexkamera vor, dann muss ich die jedoch jetzt selber noch drehen. Ich kann der Kamera also nicht sagen, sie solle sich selber plötzlich von einer FPS-Kamera in eine TPS-Kamera positionieren.

Somit sollte man meiner Auffassung nach, die Orientierung der Kamera selbst in die nutzenden Klassen unterbringen. Zumal, wenn ich mir die Codezeilen für eine Transformation eines Modus' in einen anderen ansehe, sich diese sehr in Grenzen zu halten scheinen ;)

Aber letztendlich ist das eigener Designgeschmack und funktioniert so oder so. Ich erinner mich an ein Zitat aus einem Spieleentwicklungsbuch: "If it looks right, it is right."
Solange du keine HiQ-Enterprise software designest, mach das, was du für richtig hältst. Ändern kann man jeder Zeit ;)
EnvisionGame(); EnableGame(); AchieveGame(); - Visionen kann man viele haben. Sie umzusetzen und auf das Ergebnis stolz zu sein ist die eigentliche Kunst.

RmbRT

Treue Seele

  • »RmbRT« ist der Autor dieses Themas

Beiträge: 169

Wohnort: Darmstadt

Beruf: Student

  • Private Nachricht senden

8

25.08.2014, 14:57

Danke für die Antworten.
@Schorsch: Was genau ist das Strategy pattern?
@David: Die enum Werte waren noch aus ner Bitmaske :)
@Legend: Bin gar nicht drauf gekommen :)
@Sacaldur: Ich verstehe gerade nicht genau, was du mir vorschlägst (bin gerade nicht so aufnamefähig :))
Ich denke, ich mache es mit einer Basisklasse.
MfG,
RmbRT
"Dumm ist, wer dummes tut."

dot

Supermoderator

Beiträge: 9 757

Wohnort: Graz

  • Private Nachricht senden

9

25.08.2014, 15:08

Imo sollte man zwischen der Kamera (kapselt alles, was mit dem Abbilden einer Szene auf eine Bildebene zu tun hat) und der Logik, die die Platzierung der Kamera in der Szene regelt, differenzieren. Das von Schorsch genannte Strategy Pattern ist hier imo eine gute Lösung. Da es sich um zwei grundverschiedene und unabhängige Dinge handelt, sollten die unbedingt in separate Klassen.

Ich löse es beispielsweise oft so, dass ich eine Camera hab, die die View- und Projection-Matrix verwaltet. Eine Camera kann dann mit einem Navigator verbunden werden, über den die Camera ihre Position und Ausrichtung bezieht. Dieser Aufbau erlaubt, verschiedene Implementierungen von Camera (z.B. PerspectiveCamera vs OrthogonalCamera) und Navigator (z.B. FirstPersonNavigator vs ThirdPersonNavigator) beliebig zu kombinieren.

Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von »dot« (25.08.2014, 15:13)


RmbRT

Treue Seele

  • »RmbRT« ist der Autor dieses Themas

Beiträge: 169

Wohnort: Darmstadt

Beruf: Student

  • Private Nachricht senden

10

25.08.2014, 15:20

Das mit den Pattern ist cool. :thumbsup:
Ich werde es wie dot vorgeschlagen hat mit einer Navigatorklasse machen. Danke noch mal für alle Tipps :thumbsup:
MfG,
RmbRT
"Dumm ist, wer dummes tut."

Werbeanzeige