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

birdfreeyahoo

Alter Hase

  • »birdfreeyahoo« ist der Autor dieses Themas

Beiträge: 756

Wohnort: Schorndorf

Beruf: Junior Software Engineer

  • Private Nachricht senden

1

23.09.2014, 18:21

[2D] füllende Kurve

Hallo, ich habe eine Frage/Problem. Ich habe ein C (als Kreissegment von der Form her). Dieses soll einen ablaufenden Timer darstellen.
Innerhalb von 10 Sekunden läuft er ab. ( Das C baut sich ab)
Ich habe eine Textur, die aus 16 Tiles besteht, die jeweils einen Status des C's darstellen. Jedoch ist das extrem auffällig, da man einzelne Segmente deutlich verschwinden sieht.

Ich frage mich wie die Software-Entwickler so eine flüssige Aufladebar eigentlich machen? Wird die Kurve mathematisch beschrieben und dann dynamisch generiert?

2

23.09.2014, 18:31

Die lässt sich mit Interpolation und Trigonometrie/Polynom, was man nachträglich um -90° bzw. 90° dreht, auch mathematisch erzeugen.
Ein flüssiges auf bzw entladen ist dann ja je nach Design ganz einfach.
Ich würde das so machen. :)
Eine andere Methode wäre eine Sprite-Maske, wobei dann das aufladen auch eher wie die Befüllung eines Siphons aussieht, also parallel zur X-Achse.

MfG
Check

Schorsch

Supermoderator

Beiträge: 5 145

Wohnort: Wickede

Beruf: Softwareentwickler

  • Private Nachricht senden

3

23.09.2014, 18:34

Du kannst auch verschiedene Texturen übereinander blenden. Ein Beispiel wäre du renderst das C leer (wie auch immer das bei dir aussieht). Darüber renderst du jetzt das gefüllte C. Willst du nun 50% Ladestand haben so renderst du das gefüllte C eben nur halb. Bei 33% eben nur ein Drittel davon und so weiter.
„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.“

birdfreeyahoo

Alter Hase

  • »birdfreeyahoo« ist der Autor dieses Themas

Beiträge: 756

Wohnort: Schorndorf

Beruf: Junior Software Engineer

  • Private Nachricht senden

4

23.09.2014, 19:26

@Checkmateing: Wie meinst du das genau?
@Schorsch: Dann krieg ich aber ein Auffüllen parallel zur x-Achse. Ich möchte aber, dass es so aussieht, wie wenn ich eine horizontale Timer-Bar biege.

Nimelrian

Alter Hase

Beiträge: 1 216

Beruf: Softwareentwickler (aktuell Web/Node); Freiberuflicher Google Proxy

  • Private Nachricht senden

5

23.09.2014, 20:03

Noch ein Vorschlag: Du hast einmal die Kontur vom C und einmal die Füllung.
Beide formen einen Kreis (Kontur links, Füllung rechts). Jetzt kannst du einfach die Füllung rotieren und du hast genau das, was du willst, ohne irgendwelche komplizierte Mathematik ;)

Edit: Hier nochmal als Bild mit 0% und 50%.

(Link)

(Link)
Ich bin kein UserSideGoogleProxy. Und nein, dieses Forum ist kein UserSideGoogleProxyAbstractFactorySingleton.

Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von »Nimelrian« (23.09.2014, 20:11)


birdfreeyahoo

Alter Hase

  • »birdfreeyahoo« ist der Autor dieses Themas

Beiträge: 756

Wohnort: Schorndorf

Beruf: Junior Software Engineer

  • Private Nachricht senden

6

23.09.2014, 21:03

Also ich hab keine Kontur, sondern wenn dort halt nicht gefüllt ist, ist komplett leer.
Ich würde das halt direkt im Shader/Material machen. Momentan habe ich das so gelöst, dass ich eine C-Textur habe und diese rendere und die Texturkoordinaten je nach dem Zeitwert ändere und den Renderbereich vertikal runterskaliere.
Aber dadurch füllt es sich linear auf und nicht so wie ich es gerne würde.

Ich kann versuchen ein FCanvasTriangleItem zu zeichnen (Dreiecksliste) und halt wie bei einem Kreis das Aufsetzen, bloß dass ein Segment nun ein Viereck ist (Weil das Ende nicht im Mittelpunkt ist).

7

23.09.2014, 21:20

Gegeben ist:

C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
template<typename T>
class Vec2
{
public:
    T x;
    T y;
};
//Operatoren...
template<typename VectorType>
Vec2<VectorType> interpolate(const Vec2<VectorType> v1, const Vec2<VectorType>& v2, double inter)
{
    //Funktion interpoliert zwischen zwei Punkten
    assert(0. >= inter && inter <= 1.);

    double inter1 = 1. - inter;

    return Vec2<VectorType>(inter1 * v1.x + inter * v2.x,
                            inter1 * v1.y + inter * v2.y);
}


Des Weiteren nehmen wir den Pfad des C's auch mal als gegeben an. Es ist
Versimpelt zu einem Halbkreis also repräsentiert durch folgendes Konstrukt:
Wenns nur so'n Kreis ist, dann ist das andere, beschriebene Vorgehen deutlich angenehmer.
Aber naja:

C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
void makeCircle(Vec2<float> middle, float radius, std::vector<Vec2<float>>& circleVec, unsigned int phiBegin = 0U, unsigned int phiEnd = 360U)
{
    //wenn der angegebene Beginner-Winkel größer als der End-Winkel ist,
    //macht dieser Funktionscall schon keinen Sinn mehr. 
    if(phiBegin > phiEnd)
        throw std::invalid_argument("phiBegin needs to be smaller than phiEnd!");

    //Durchlaufe alle gewollten Winkel
    for(unsigned int phi = phiBegin; phi < phiEnd; ++phi)
    {
        Vec2<float> v;
        v.x = radius * std::sin(phi * 3.1415926f / 180.0f) + middle.x;
        v.y = radius * std::cos(phi * 3.1415926f / 180.0f) + middle.y;

        //Füge Winkel dem angegebenem Container hinzu
        circleVec.push_back(v);
    }
}

Der Übergang lässt sich nun recht leicht machen.

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
void getNewPath(const std::vector<Vec2<float>>& fromPath, float percent, std::vector<Vec2<float>>& newPath, int depth = -2)
//depth beschreibt wie kleinschrittig gearbeitet werden soll. Wert ist der Exponent zur Basis 10
//-2 ist schon ziemlich klein schrittig
{
    //Wenn die angegebenen Prozent an Fülle nicht größer 0 und kleiner 1 sind, läuft was schief
    if(0.f > percent || percent > 1.f)
        throw std::invalid_argument("percentage is wanted. Something smaller 0 or bigger 1 is no percentage");
    //Ist die Tiefe größer 0, gehen wir in mehr als 100% Schritten weiter.... Macht keinen Sinn. Schon depth=0 macht eher selten Sinn
    if(depth > 0)
        throw std::invalid_argument("depth should be smaller than 1");

    //Punkt ermitteln, der auf dem Graphen liegt und zum Prozentwert passt
    unsigned int index0 = static_cast<unsigned int>(percent * fromPath.size());

    //Wenn der nächste Index benutzbar ist, kanns losgehen
    if(index0 + 1U < fromPath.size())
    {
        //Nun wird der Wert ermitteln, zwischen dem interpoliert wird, schließlich bleiben eventuell noch ein paar Promille über
        unsigned int index1 = index0 + 1U;

        //Um einen Prozentwert relativ zu diesen zu interpolierenden Punkten zu erhalten, müssen wir unseren angegebenen erst einmal "konvertieren"
        double pathPercent = static_cast<double>(index0) / fromPath.size();

        //Bis zu index0 kommt aber auf jeden Fall alles vom alten in den neuen Pfad
        newPath.insert(newPath.end(), fromPath.begin(), fromPath.begin() + index0);

        //Solange i nun kleiner als unser "konvertierter" Wert ist, interpolieren wir
        for(double i = 0.; i < percent - pathPercent; i += std::pow(10, depth))
        {
            // i gibt nun unsere Prozent an, die wir interpolieren
            newPath.push_back(interpolate(fromPath[index0], fromPath[index1], i));
        }
    }
    else
    {
        //Der neue Pfad ist der alte Pfad, nichts hat sich geändert.
        newPath.insert(newPath.end(), fromPath.begin(), fromPath.end());
    }
}

Den Pfad zeichnest du dann als fette Linie oder so, das obliegt ja dann dir. Um eine Neigung zu erzeugen, muss natürlich noch ein wenig gerechnet werden.

MfG
Check

EDIT:

Zitat

Aber dadurch füllt es sich linear auf und nicht so wie ich es gerne würde.

Wie jetzt? Gehts doch um tweening?

Dieser Beitrag wurde bereits 3 mal editiert, zuletzt von »Checkmateing« (24.09.2014, 00:20)


birdfreeyahoo

Alter Hase

  • »birdfreeyahoo« ist der Autor dieses Themas

Beiträge: 756

Wohnort: Schorndorf

Beruf: Junior Software Engineer

  • Private Nachricht senden

8

23.09.2014, 23:24

Danke für die Erläuterung!
Also makeCircle liefert eine Sammlung von 360 Punkten auf dem Kreis? Diese kann ich ja mit Linien verbinden.
Deine getNewPath-Funktion verstehe ich nicht so ganz, sorry.

Zu deinem Edit: Ich möchte halt nicht, das sich sozusagen das C auffüllt, also wie wenn eine Linie die y-Achse hochwandert, sondern dass es an dem einen Ende anfängt und an dem anderen aufhört (unabhängig deren Höhe).

9

24.09.2014, 00:23

Hab mal Kommentare eingefügt...
Wenn dus dir einfach machen willst kannst du das einmal für die innere Kurve und einmal für die äußere Kurve machen, also dir den Pfad holen.
Das dann nochmal ordnen und per TriangleStrip rendern. voilá

Für vereinfachten Umgang mit dem ganzen Interpolieren hier und da kannst du auch z.B. diese Klasse von Yggdrasil nehmen, was den Algo oben recht gut vereinfachen würde.
*eigenwerbung-stinkt-ja-ich-weiß*

MfG
Check

dot

Supermoderator

Beiträge: 9 757

Wohnort: Graz

  • Private Nachricht senden

10

24.09.2014, 10:21

Wie renderst du das Ganze denn? Falls du einen Fragment Shader hast, könntest du die "Füllung" dort drin berechnen... ;)

Werbeanzeige