Willkommen ~Gast!
Registrieren || Einloggen || Hilfe/FAQ || Staff
Probleme mit der Registrierung im Forum? Melde dich unter registerEin Bild.
Autor Beitrag
000
26.07.2000, 14:51
Renegade



Hallo Leute, ich hätte da gerne ein Problem:

Kann mir einer sagen, wie man einen Fortschrittsbalken coden kann? Ziel ist es, eine Funktion (im Prinzip einen einfachen Button) erst dann zu aktivieren, wenn der Fortschrittsbalken bei 100% ist. Die Geschwindigkeit die der Balken voranschreitet, soll von dem Eigenschaftswert eines Spielers abhängen.

Das ganze soll in etwa so aussehen wie in RogueSpear, wenn man z.B. eine Ladung an einer Tür montiert.

Vielen Dank für die Mühe, die ich Euch gemacht hab.

--

zum Seitenanfang zum Seitenende Profil || Suche
001
27.07.2000, 02:35
Prefect



Also.. als erstes kopierst du ein _einfaches_ bereits existierendes HUD-Element (z.B. CHudBattery oder CHudTrain). Die Dinge die zu kopieren sind:
- die Klassendefinition in hud.h
- die Sourcedatei (battery.cpp / train.cpp)
- die Membervariable in CHud (CHud::m_Battery)
- die Aufrufe von m_Battery/m_Train.Init() und .VidInit() (in CHud::Init() und CHud::VidInit())

Dann löschst du aus deinem neuen HUD-Element _alles_ aus VidInit() raus (außer dem return 1;), und _alles_ aus Draw() (wieder außer dem return).
Dann löscht du _alle_ Membervariablen des HUD-Elements und fügst stattdessen eins ein: float m_flPercentage; und float m_flSpeed;
Dann überschreibst du die Init()-Funktion:
Sie muß m_flPercentage, m_flSpeed, m_iFlags auf 0 setzen, und AddHudElement aufrufen (ist schon drinnen).
Dann nennst du die Message um:
- MsgFunc_Battery/MsgFunc_Train umbenennen (sowohl in der .cpp als auch in hud.h)
- Die HOOK_MESSAGE und DEFINE_MESSAGE statements auch entsprechen umbenennen

Die neue Message soll einfach ein Byte entgegen nehmen (die Geschwindigkeit in Prozent pro Sekunde). Du löscht dazu den Hauptteil der Funktion (nach BEGIN_READ bis vor return) und programmierst ihn so um, dass
- m_flSpeed gesetzt wird (READ_BYTE)
- m_flPercentage auf 0 gesetzt wird
- m_iFlags |= HUD_ACTIVE;

Ok. Jetzt kommt die Draw()-Funktion. Der erste Teil der Draw()-Funktion setzt die m_flPercentage hoch:
m_flPercentage += flTime * m_flSpeed;
if (m_flPercentage >= 100)
{
m_iFlags &= ~HUD_ACTIVE; // bar filled up completely
}

Der Drawingteil besteht dann prinzipiell aus
int r, g, b, a;
UnpackRGB(RGB_YELLOWISH, r, g, b);
a = MIN_ALPHA;
ScaleColors(r, g, b, a );

Dann hast du die RGB-Werte. Der Rest ist dann (je nachdem wie der Balken aussehen soll) eine Ansammlung von mehreren FillRGBAs(). Nachdem ich den Rest sooo ausführlich erklärt habe, darfst du dir das selber überlegen (Um Fragen vorzubeugen: Wußtest du, das man die Aufrufkonventionen für FillRGBA() in den Headern nachschauen kann?).

Jetzt kommt die Serverseite. Zunächst einmal mußt du am Anfang von player.cpp eine neue int gmsgMsgName = 0; einfügen (das sollte ziemlich selbsterklärlich sein, die gibt's massenhaft). Dann suchst du nach LinkUserMessages() und fügst da (ebenfalls sehr offensichtlich) eine eigene Anweisung ein:
gmsgMsgName = REG_USER_MSG("MsgName", 1); // this msg is 1 byte long

Dann mußt du nur noch an den Stellen, an denen der Balken ausgelöst werden soll, Code in der Art einfügen:
MESSAGE_BEGIN(MSG_ONE, gmsgMsgName, NULL, pev);
WRITE_BYTE(50); //takes 2 secs to fill
MESSAGE_END();

[rantmode]
Achja, um dummen Fragen vorzubeugen: Dieser Code geht natürlich davon aus, dass du ihn aus dem Kontext von CBasePlayer aus ausführst. Wenn du das von einer anderen Entity aus machst, wird HL wahrscheinlich abstürzen (klar, ein Monster oder eine Waffe ist kein Netzwerkclient und kann deswegen keine Nachrichten empfangen). Also stell vorher sicher, dass du aus dem Kontext eines CBasePlayer diesen Code ausführst. Natürlich kannst auch einfach pev durch pPlayer->pev ersetzen (wiederum, um dummen Fragen vorzubeugen: Dies geht natürlich nur, wenn pPlayer definiert ist und auf einen gültigen Player zeigt).

Achja, um noch einer potentiellen dummen Frage vorzubeugen: Wenn dieser Code nicht in player.cpp steht, ist gmsgMsgName natürlich nicht definiert. Du mußt es deshalb irgendwo über diesem Code global (d.h. außerhalb einer Funktionsdefinition, am Bestem am Anfang der Datei) importieren mit: extern gmsgMsgName;

PS TheVoice: Ist es dass, was du mit schlecht gelaunt meinst? *g*
[/rantmode]

cu,
Prefect

--

Widelands - Gemütliche Aufbaustrategie, Free Software
Noch ein Blog - Lerne, wie die Welt wirklich ist, aber vergiss niemals, wie sie sein sollte.

zum Seitenanfang zum Seitenende Profil || Suche
002
27.07.2000, 09:58
Renegade



Hey hey hey,

hab vielen Dank!

Gut der Mann!

:)

--

zum Seitenanfang zum Seitenende Profil || Suche
003
30.07.2000, 01:49
Blackpanther



Ich hab mir das mal grad durchgelessen WOW soviel Fachwiesen is ja ab artig habs nach der 10 Zeile nich mehr geblickt . Machste sowas beruflich oder so das is ja abartig .

Respeckt allen Codern das muß mal wieder esagt werden !

--

zum Seitenanfang zum Seitenende Profil || Suche
004
31.07.2000, 18:09
Hyperion



Hi!
Ich hatte letztens das gleiche Problem. Ich stelle mal kurz meine Lösung vor. Ich lass den ganzen Message-Kram weg, den hat Prefect ja schon erklärt. Allerdings hab ich nicht (wie von Prefect angedeutet) Rechtecke benutzt, sondern habe mich von der Taschenlampe von Standard-HL "inspirieren" lassen. Dass macht zwar auf den ersten Blick nicht soviel Sinn, weil die Sprites von Standard-HL alle durchsichtig sind, und das gerade bei einem Fortschrittsbalken nicht von Vorteil ist, aber man kann ja auch per SPR_DrawHoles Sprites zeichnen, die GIF89a-mäßig funktionieren. Und man kann mit frei wählbaren Sprites seine Kreativität besser ausleben. Also, legen wir erstmal los (das wird schon fast Tutorial-artig). Wir definieren eine Klasse in der <B>hud.h</B>:

[code]
class CHudTimeBar: public CHudBase
{
public:
int Init( void ); // Initialisieren
int VidInit( void );
int Draw( float flTime ); // die Draw-Funktion
void Reset( void ); // Reset....
int MsgFunc_TimeBar(const char *pszName, int iSize, void *pbuf ); // die Message an sich

private:
HSPRITE m_hSpri

--

zum Seitenanfang zum Seitenende Profil || Suche
005
31.07.2000, 18:18
Hyperion



Hi!
Ich hatte letztens das gleiche Problem. Ich stelle mal kurz meine Lösung vor. Ich lass den ganzen Message-Kram weg, den hat Prefect ja schon erklärt. Allerdings hab ich nicht (wie von Prefect angedeutet) Rechtecke benutzt, sondern habe mich von der Taschenlampe von Standard-HL "inspirieren" lassen. Dass macht zwar auf den ersten Blick nicht soviel Sinn, weil die Sprites von Standard-HL alle durchsichtig sind, und das gerade bei einem Fortschrittsbalken nicht von Vorteil ist, aber man kann ja auch per SPR_DrawHoles Sprites zeichnen, die GIF89a-mäßig funktionieren. Und man kann mit frei wählbaren Sprites seine Kreativität besser ausleben. Also, legen wir erstmal los (das wird schon fast Tutorial-artig). Wir definieren eine Klasse in der hud.h:

class CHudTimeBar: public CHudBase
{
public:
int Init( void ); // Initialisieren
int VidInit( void );
int Draw( float flTime ); // die Draw-Funktion
void Reset( void ); // Reset....
int MsgFunc_TimeBar(const char *pszName, int iSize, void *pbuf ); // die Message an sich

private:
HSPRITE m_hSprite1; // speichert Sprite 1
HSPRITE m_hSprite2; // Sprite 2
wrect_t *m_prc1; // Zeiger auf Sprite 1
wrect_t *m_prc2; // Zeiger auf Sprite 2
double m_flTimeDiff; // Zeit-Differenz
double m_flTimeEnd; // Wann ist der Balken am Ende?
double m_flTimeScale; // Zeiteinheit
double m_flScale; // Skalierung des Auschnitts
int m_iWidth; // Breite des Ausschnitts
};

Sprite 2 brauchen wir nicht zwingend, ist aber sicher schöner, so haben wir einen Rahmen für unseren Balken. Ich mach's jetzt trotzdem erstmal ohne. Da könnt ihr Euch dann nochmal dran auslassen.
Kurz zum Konzept: Wie ihr seht, habe ich die Taschenlampe kopiert und angepasst. Die verfährt so, dass sie mit Hilfe von Zeigern und Referenzen nur einen bestimmten Ausschnitt des Energie-Sprites ausgibt. Allerdings haben die es sich recht einfach gemacht und übermitteln pro Änderung ein Byte mit mit dem neuen Stand und haben nur eine Skala von 0..100. Das belastet zum einen den Netzverkehr (ok, bei einem Byte alle paar Sekunden ist das nicht so wild) und ist zum anderen unflexibel. Wir wollen das universeller. Uns wird ein einziges mal eine Anzahl Sekunden übermittelt, und von diesem Zeitpunkt aus an zählen wir selbst herunter und brauchen keine weiteren Informationen. Ok, hier kommt wieder Quelltext. Legt am besten eine neue Datei (timebar.cpp) an:

#include "hud.h"
#include "cl_util.h"
#include "parsemsg.h"

#include <string.h>
#include <stdio.h>

Standard-include-ments.

DECLARE_MESSAGE(m_TimeBar, TimeBar)

Zur Message erklären.

int CHudTimeBar::Init(void)
{
HOOK_MESSAGE( TimeBar );

m_flTimeDiff = 0;
m_flTimeScale = 0.0;
m_flTimeEnd = 0.0;
m_flScale = 0.0;

m_iFlags |= HUD_ACTIVE;

gHUD.AddHudElem(this);

return 1;
};

Die Init-Funktion müssen wir noch in der Init der hud.cpp aufrufen lassen. Sie hakt erstmal unsere Message, Resettet alle Variablen und fügt unseren TimeBar dem HUD hinzu.

void CHudTimeBar::Reset(void)
{
m_flTimeDiff = 0;
m_flTimeScale = 0.0;
m_flTimeEnd = 0.0;
m_flScale = 0.0;
}

Reset, sollte klar sein :)

int CHudTimeBar::VidInit(void)
{
int HUD_flash_empty = gHUD.GetSpriteIndex( "flash_empty" );
int HUD_flash_full = gHUD.GetSpriteIndex( "flash_full" );

m_hSprite1 = gHUD.GetSprite(HUD_flash_empty);
m_hSprite2 = gHUD.GetSprite(HUD_flash_full);
m_prc1 = &gHUD.GetSpriteRect(HUD_flash_empty);
m_prc2 = &gHUD.GetSpriteRect(HUD_flash_full);

m_iWidth = m_prc2->right - m_prc2->left;

return 1;
};

Muss auch noch in die hud.cpp eingetragen werden (siehe Prefect). Hier wird es interessant. Wie ich schon vorhin sagte, werden hier nun m_hSprite1 und 2 die Sprites zugewiesen (hier noch die Taschenlampen-Sprites, ändern!), und die m_prc's verweisen nun als Zeiger auf die Sprites. m_iWidth ist die Breite des Sprites (ist also auch hier flexibel, es kann jedes beliebiges Sprite genommen werden!).

int CHudTimeBar:: MsgFunc_TimeBar(const char *pszName, int iSize, void *pbuf )
{

BEGIN_READ( pbuf, iSize );

short x = READ_SHORT();
m_flTimeDiff = x;
m_flTimeScale = 1.0 / m_flTimeDiff;
m_flTimeEnd = gHUD.m_flTime + m_flTimeDiff;

return 1;
}

Die Message kommt an: und zwar als Short. Short ist 2 Byte groß und beinhaltet den Zahlenraum von 0..65536, in Sekunden wären das ca. 18 Stunden, und das sollte für eine Runde (Teamplay) oder Entwicklungs- oder Respawnzeit genügen :). Ein Byte reicht nicht, da die Werte mit 256 Sekunden ein wenig knapp wären (4,2 Minuten...). Also: Short kommt an und wird in m_flTimeDiff gespeichert. Nun erstellen wir in m_flTimeScale die "Einheiten" für unseren Balken, indem wir 1 durch den empfangenen Wert teilen. Nun Stoppen wir noch die Zeit und addieren die zusätzlichen (gerade erhalenen) Sekunden drauf. gHUD.m_fl_Time enthält genau wie in der hl.dll bzw. mp.dll die gpGlobals->time die aktuelle Zeit (neuerdings im double-Format).

int CHudTimeBar::Draw(float flTime)
{
if ( gHUD.m_iHideHUDDisplay & HIDEHUD_ALL )
return 1; // geh raus, falls unerwünscht

if (!(gHUD.m_iWeaponBits & (1<<(WEAPON_SUIT)) ))
return 1; // geh raus, falls keine Weaponsuit vorhanden

int r, g, b, x, y, a;
wrect_t rc;

m_flScale = (m_flTimeEnd - gHUD.m_flTime) * m_flTimeScale; // wir messen unsere Position ab

a = MIN_ALPHA;

if (m_flScale < 0.10)
UnpackRGB(r,g,b, RGB_REDISH);
else
{
UnpackRGB(r, g, b, RGB_YELLOWISH);
}

ScaleColors(r, g, b, a);

y = (m_prc1->bottom - m_prc2->top)/2; // Knapp unter dem Bildschirmrand
x = ScreenWidth/2 - m_iWidth/2 ; // in der Mitte des Bildschirms

int iOffset = m_iWidth * (1.0 - m_flScale); // übertragen der Skalierung auf das Sprite
if (iOffset < m_iWidth)
{
rc = *m_prc2;
rc.right -= iOffset; // von rechts nach links fallen

SPR_Set(m_hSprite2, r, g, b );
SPR_DrawAdditive( 0, x - iOffset, y, &rc); // und Sprite setzen
}

return 1;
}

Also, ihr seht, ihr habt nun einen voll funktionstüchtigen Fortschrittsbalken, den ihr für alle Möglichen Zwecke einsetzen könnt. Es ist aber immernoch Arbeit angesagt, z.B. ist vielleicht die Richtung nicht euren Wünschen entsprechend, oder er soll nicht fallen sondern steigen, die Sprites sind sowieso noch nicht gut, zweites Sprite einbauen (als Kasten bzw. Bett), mp.dll bzw. hl.dll verändern...

Ich hoffe aber trotzdem, ich konnte euch helfen.

Achja: noch ein paar Sachen.
-Alles fand in der client.dll statt. Holzauge sei Wachsam!
-Um Knöpfe oder andere Dinge zu aktivieren muss man in der mp.dll bzw. hl.dll parallel mitzählen uns stoppen. Je nachdem beim Player, in der Gamerules-Think oder bei einem speziellen Item oder Monster. Also: dazu gehört noch mehr!
-Ich hasse die client.dll.
-Ist gut Du, Momboutu!

--

zum Seitenanfang zum Seitenende Profil || Suche
006
31.07.2000, 18:27
Hyperion



Hey, warum ist mein Post zweimal? Ich schwöre, ich hab nur einmal geklickt! Das Forum hat aber schon vorher rumgesponnen, meine schönen <pre>[code]</pre> Zeichen sind alle verloren gegangen und ich musste sie wieder entfernen!

--

zum Seitenanfang zum Seitenende Profil || Suche