webForumDet fria alternativet

Nu blir jag virrig...

15 svar · 449 visningar · startad av Alpha II

Alpha IIMedlem sedan maj 20002 993 inlägg
#1

Hej,

försöker komma på någon snygg funktion som går igenom en länkar lista och letar efter objekt som uppfyller vissa vilkor (kollar avstånd mellan deras positioner och spelarens sikte, det är ett spel :D ). Problemet är att jag inte vet hur jag ska returnera listan med pekare.

Jag tänkte först att jag skulle göra en funktion som tog en CParticle pekare och en pekare till en int som argument. Sen skulle minne allokeras i CParticle för att hålla adresserna till n antal objekt.

Problemet är att CParticle är en helt virituell klass som har många andra små klasser som ärver ifrån den så det går inte att allkoera CParticle objekt.

Jag är inte alls säker på att jag har gjort allt detta på rätt sätt, det är väldigt mycket krångel och konstiga koder för att skapa objekt till den länkade listan.

Såhär ser den ej fungerande funktionen ut.


void CParticleEngine::GetNodesAt(vec2f point, CParticle *nodes, int *num)
{
	CParticle *node = m_pBase;
	*num = 0;

	while(node!=NULL)
	{

		int x = int(node->pos.x);
		int y = int(node->pos.y);
		int size = int(node->size);

		float deltaXSqr = (float)pow(x - point.x, 2);
		float deltaYSqr = (float)pow(y - point.y, 2);
		float sumRadii	= (float)pow(size + 50, 2);

		if(deltaXSqr+deltaYSqr < sumRadii && node->killable)
		{

			*num++;
			nodes = new CParticle[*num];
			nodes[*num-1] = node;
		}

		node = node->next;

	}
}

Sen när jag vet vilka objekt som hittas skulle jag också behöva komma åt medlemsvariabler som finns i de klasser som ärver från CParticle.

Såhär skapar jag fö partiklar:

void CrateNewFeatherEmitter(CParticleEngine *pEngine, float x, float y, int size, CTextureAnim *texture)
{

	for(int i=0;i<5;i++)
	{

		CParticle *p = new CParticleFeather;

		p->pos		= vec2f(x, y);
		p->vel		= vec2f((float)random(-50,50), (float)random(-50,50));
		p->gravity	= vec2f(0.0f, -100.0f);
		p->size		= random(size,size+20)/10.0f;
		p->life		= 5;

		CParticleFeather *pFeather;
		pFeather = (CParticleFeather*)p;
		pFeather->textureAnim = *texture;
		pFeather->textureAnim.Randomize();

		pEngine->CreateNew(p);

	}

}

Finns det något smartare sätt att göra detta på? Jag har inte så bra koll på hur man använder klasser på ett lite mer avancerat sätt.

Sang-draxMedlem sedan juli 2002581 inlägg
#2

list<CParticle*> borde väl vara lösningen om CParticle är en abstrakt klass.

Det är inte smart att blanda ihop två funktioner i en klass, dvs CParticle innehåller next för att skapa en länkad lista.

Konverteringen gör du med dynamic_cast<>().

tmbMedlem sedan feb. 200328 inlägg
#3

Eller vector<CParticle*> om du tänker accessa den som ett array vilket torde vara lite effektivare än list<>.

Som Sang-drax skriver blir du nog tvungen att köra dynamic_cast på pekarna. dynamic_cast bör man dock undvika om det är möjligt eftersom det är en kostsam funktion i exekveringstid räknat.

Kan du inte ha en virtuell funktion i basklassen CParticle som varje deriverad klass sätter och returnerar ett unikt värde för sin klass? Då kan du använda den funktionen till att identifiera klassen och köra en vanlig cast direkt om det behövs, typ:

if ( KLASS1 == pekare->klassTyp() )
  nypekare = ( KLASS1 * )pekare;
else if ( KLASS2 == pekare->klassTyp() )
  nypekare = ( KLASS2 * )pekare;

Lite som "fattigmans" implementation av dynamic_cast :D

Sang-draxMedlem sedan juli 2002581 inlägg
#4

Alpha skrev ju att det skulle vara en länkad lista -- helt korrekt eftersom en länkad lista är lämplig att hålla object till ett spel, där object ofta tas bort och läggs till.

Din "fattigmans-implementation" är med all säkerhet långsammare än dynamic_cast<>(). Du tänker att dynamic_cast är långsam, drar slutsatsen att den bör undvikas och skriver slutligen en egen variant för attt uppnå samma sak. Gör inte så.

tmbMedlem sedan feb. 200328 inlägg
#5

Ah, min miss. Jag förstod det som att han ville ha tillbaka pekarna i ett array för att hantera dem och inte för att ta bort eller lägga till saker. Om det är det första han vill så är vector<> bättre och i det senare givetvis en list<>.

RTTI är långsamt. Om man läser runt lite på nätet och i de böcker jag läst om C++ så säger de samma sak.

Bara två av de länkar jag hittar om det:

http://www.devx.com/getHelpOn/Article/10202/0/page/3

"Although the use of dynamic_cast solves this problem neatly, it exacts a toll. As opposed to typeid, a dynamic cast isn't a constant time operation. In order to determine whether the cast can be performed, dynamic_cast must traverse the derivation lattice of its argument at runtime. Therefore, use dynamic_cast judiciously."

http://www.codeguru.com/cpp/tic/tic0275.shtml
(Vilket fö verkar vara en bra tutorial på RTTI. Sparad :D)

"Because the library routine used for dynamic_cast must check through a list of base classes, the overhead for dynamic_cast is higher than typeid( ) (but of course you get different information, which may be essential to your solution), and it’s nondeterministic because it may take more time to discover a base class than a derived class. In addition, dynamic_cast allows you to compare any type to any other type; you aren’t restricted to comparing types within the same hierarchy. This adds extra overhead to the library routine used by dynamic_cast."

De flesta säger samma sak. Gör man spel så är ju i de flesta fall snabbhet viktigt och löser man det själv så går det att komma runt dynamic_cast. Att själv kolla mot ett typid och sedan göra en egen cast är nog snabbare än dynamic_cast.

Men det är naturligtvis upp till den som skriver koden att avgöra vilken lösning som är den bästa för den uppgift man skall lösa.

spangoMedlem sedan juni 20008 205 inlägg
#6

tmb skrev:

De flesta säger samma sak. Gör man spel så är ju i de flesta fall snabbhet viktigt och löser man det själv så går det att komma runt dynamic_cast. Att själv kolla mot ett typid och sedan göra en egen cast är nog snabbare än dynamic_cast.

Att typeid är snabbare än dynamic_cast säger bara att typeid har en enklare uppgift än dynamic_cast. Det är inte det att dynamic_cast är dåligt, utan att det den gör tar mer tid.

Om det bara är att identifieria man är ute efter är typeid finemang att använda - om man bortser från att det inte är snyggt - men (som de säger i artikeln på codeguru) det är inte alltid det är vad man vill. Ska man ha klasshierarkier och kolla om ett givet objekt är en instans av en direkt eller indirekt subklass till en annan klass har jag svårt att tro att man vinner något på det. Däremot blir det mer och/eller osmidigare kod.

Sedan ska man i det perfekta fallet aldrig behöva göra en narrowing conversion i ett OO-program (utan använda sig av polymorfism), men det är en annan sak ;)

tmbMedlem sedan feb. 200328 inlägg
#7

Nä, naturligtvis är dynamic_cast inte dåligt och kan jag inte se att jag påstått heller men den har en kostnad (i exekveringstid) som man bör vara medveten om vilken kan vara kostbar om den används väldigt mycket. Jag använder själv dynamic_cast men så gör jag inte spel heller (även fast jag helst skulle vilja. Det var ju därför jag en gång i tiden började programmera) :D

Sedan ska man i det perfekta fallet aldrig behöva göra en narrowing conversion i ett OO-program (utan använda sig av polymorfism), men det är en annan sak

Nä, verkliga världen är sällan snäll mot försök att få total konsekvens i allt :D Det är alltid en fråga om avvägning och det är väl där vanan som programmerare spelar in antar jag. Man blir bättre och bättre på att välja rätt lösning för varje problem.

spangoMedlem sedan juni 20008 205 inlägg
#8

Problemet är väl det att RTTI öht har kostnad, inte bara dynamic_cast.

tmb skrev:

Nä, verkliga världen är sällan snäll mot försök att få total konsekvens i allt :D Det är alltid en fråga om avvägning och det är väl där vanan som programmerare spelar in antar jag. Man blir bättre och bättre på att välja rätt lösning för varje problem.

"If the facts don't fit the theory, change the facts." :e

tmbMedlem sedan feb. 200328 inlägg
#9

Det har du rätt i. Men fördelen är ju att den kan lösa en del kniviga problem utan att man behöver koda ihjäl sig :D

"If the facts don't fit the theory, change the facts."

Haha, ja, den är ju bara för bra :D

Sang-draxMedlem sedan juli 2002581 inlägg
#10

tmb skrev:

RTTI är långsamt. Om man läser runt lite på nätet och i de böcker jag läst om C++ så säger de samma sak.

Alltså att hävda att man inte ska använda dynamic_cast i spel för att det är för långsamt är verkligen att optimera på helt fel sak.

Jag har gjort ett spel och använt mig av dynamic_cast i det. Du kan få källkoden och köra en profiler på den fär att kontrollera hur sor del av tiden som spenderas i dynamic_cast. Om den inte är mindre än en PPM så skulle jag bli mycket förvåndad.

Du säger att man inte ska använda dynamic_cast, men skriver en egen funktion som gör samma sak. På en kompilator som inte gjorde några optimeringar alls, skulle du kanske (jag säger kanske) göra en lite vinst, men med dagens kompilatorer skulle effekten kanske bli den motsatta.

Dessutom skall man INTE skriva om redan befintliga funktioner i språket. Man skall inte skriva sina egna string-, list- och vector-klasser för att tjäna bråkdelar av mikrosekunder. Om programmet är så tidskrävande (vilket inte ens spel kommer i närheten av) ska man skriva sitt program i assembler (eller möjligen C). C++ är ett högnivåspråk och skall betraktas som ett sådant.

Sang-draxMedlem sedan juli 2002581 inlägg
#11

spango skrev:

Sedan ska man i det perfekta fallet aldrig behöva göra en narrowing conversion i ett OO-program (utan använda sig av polymorfism), men det är en annan sak ;)

Hur menar du då att jag skall lösa detta problem?

Object
   |
   V
 [...]
   |
   V
Airplane

class Airplane:
  public Vehicle
{
  void flySomewhere();
};

list<Object*> gameObjects;

void flyEveryAirplaneSomewhere()
{
  //Fly every Airplane somewhere
  for (o = gameObjects.begin(); o < gameObjects.end(); ++o)
    if (Airplane* air = dynamic_cast<Airplane*>(*o))
      air->flySomewhere();
}
spangoMedlem sedan juni 20008 205 inlägg
#12

Sang-drax skrev:

Hur menar du då att jag skall lösa detta problem?

spango skrev:

"If the facts don't fit the theory, change the facts." ;)

Det "perfekta fallet" är det där imaginära fallet, där man inte behöver göra ful-lösningar, avvägningar eller kompromisser av något slag. Du vet, det där fallet som inte finns...

tmbMedlem sedan feb. 200328 inlägg
#13

Alltså att hävda att man inte ska använda dynamic_cast i spel för att det är för långsamt är verkligen att optimera på helt fel sak.

Jag tror inte jag har påstått att man _inte_ skall använda det i ett spel. Jag skrev 'bör' vilket kanske låter mer definitivt än avsett. Om du uppfattar det så så har jag uttryckt mig oklart och ber om ursäkt för det. Jag flaggar för att dynamic_cast inte är gratis exekveringsmässigt och att man kan undvika den om man inte absolut behöver den för att spara tid (om det är nödvändigt OCH givetvis, man verkligen gör ett bättre jobb).

Jag har gjort ett spel och använt mig av dynamic_cast i det. Du kan få källkoden och köra en profiler på den fär att kontrollera hur sor del av tiden som spenderas i dynamic_cast. Om den inte är mindre än en PPM så skulle jag bli mycket förvåndad.

Du har säkert rätt i att din kod spenderar lite tid i dynamic_cast men hur ofta anropar du den? Ett par tusen ggr i sekunden eller mer? 100000 ggr? Eller kanske bara ett par gånger under hela körningen. Hur stor tidsåtgång man får är ju beroende av hur ofta man anropar funktionen. Och hur många gånger du använder den är beroende av vad du gör. Jag kan inte uttala mig om huruvida ditt spel är något man kan betrakta som ett spel av normal typ så jag kan inte på något vis avgöra hur bra din lösning är i andra sammanhang. Därför så har jag nog ingen nytta av din källkod för att avgöra om man alltid skall använda dynamic_cast.

Du säger att man inte ska använda dynamic_cast, men skriver en egen funktion som gör samma sak.

Nej, jag säger att man kan undvika den om man vill.

På en kompilator som inte gjorde några optimeringar alls, skulle du kanske (jag säger kanske) göra en lite vinst, men med dagens kompilatorer skulle effekten kanske bli den motsatta.

Så... min lösning skulle _kanske_ vara snabbare med en kompilatorinställning medan din skulle _kanske_ vara snabbare med en annan...

På vilket sätt stöder det du skriver ovan ditt argument att man _alltid_ skall använda dynamic_cast?

Dessutom skall man INTE skriva om redan befintliga funktioner i språket. Man skall inte skriva sina egna string-, list- och vector-klasser för att tjäna bråkdelar av mikrosekunder.

Skall? Jag skulle säga 'bör'. Det är väl upp till var och en att göra efter eget gottfinnande om de löser problem på ett effektivare sätt än de inbyggda funktionerna?

Om programmet är så tidskrävande (vilket inte ens spel kommer i närheten av)

Hur vet du det? Är inte tidsåtgången väldigt beroende av hur snabb datorn är?

ska man skriva sitt program i assembler (eller möjligen C). C++ är ett högnivåspråk och skall betraktas som ett sådant.

Tja, kan jag komma på en lösning som håller mig kvar i C++ utan att behöva gå över till några andra språk så använder iaf jag den. Varför plåga mig själv med andra språk bara för att man inte skall få hitta på sina egna lösningar på problem som inte språkets inbyggda funktionalitet löser för mig på bästa sätt?

Alltså: kontentan är att man visserligen skall försöka använda de verktyg som språket ställt till förfogande men om man finner att de inte ger bästa resultatet (och man är beroende av det) så skall man givetvis få försöka göra det bättre själv. Notera att jag inte påstått att man verkligen kommer att lyckas. Gör man inte det så kan man kanske fundera på att ändra algoritmen eller gå över till något snabbare språk (även om jag tror att det är få idag som skriver assembler som ger bättre maskinkod än en kompilator gör när den optimerar fullt ut. Iaf för större kodsegment).

Men detta är ju mina högst personliga åsikter. YMMV (vilket det helt klart gör :D).

PeWMedlem sedan juni 200010 432 inlägg
#14

Vad är bäst. Äpplen eller päron? Tja, det beror väl på vad man ska ha dem till.. en äppelpaj skulle inte smaka äppelpaj om den gjordes med päron ;)

Jag skulle inte vilja påstå att omtypningar är att föredra ö.h.t, men om det inte går att lösa på något annat sätt så varför inte? Till synyvene og sist är inte C++ ett rent OOP-språk och när det inte går att tillämpa ren OOD kommer genvägarna fram. Mao, så beror det mycket på vilken design man antagit.

tmbMedlem sedan feb. 200328 inlägg
#15

Exakt.

Hehe, äppelpaj har jag ätit men aldrig päronpaj. Undrar hur det smakar... :D

Sang-draxMedlem sedan juli 2002581 inlägg
#16

OK, Det jag menade var att i dagens läge är det mer och mer så att processortid är billigt men programmerartid är det inte, eftersom processorerna till skillnad från programmerare blir dubbelt så snabba var 18:e månad.
Därför bör man inte skriva kod som redan finns, dvs använda befintliga konstruktioner i så stor utsträckning som möjligt och det är just det som högnivåspråk handlar om.

140 ms totalt · 3 externa anrop · v20260731065814-full.beb2e261
0 ms — hämta forumlista (cache)
0 ms — hämta statistik (cache)
137 ms — hämta tråd, inlägg och bilagor (db)