Check alle échte Black Friday-deals Ook zo moe van nepaanbiedingen? Wij laten alleen échte deals zien

[OO/GameDesign(XNA+C#) Logische volgorde aanroep tot tekenen

Pagina: 1
Acties:
  • 464 views sinds 30-01-2008
  • Reageer

  • roy-t
  • Registratie: Oktober 2004
  • Laatst online: 17-10 16:43
Ik vroeg me af wat nu een logische aanroep tot tekenen is in een game. Ik vermoed dat dit redelijk open is voor discussie (ga gerust je gang)

Maar waar ik zelf over dacht was het volgende.

code:
1
2
3
4
5
6
7
8
9
10
11
Object (bijvoorbeeld een huisje) staat in het level

Huisje staat in de main draw functie
->
Draw vraagt aan huisje of deze getekend wil worden
(bijv doormiddel van een array waarin bijgehouden wordt welke objecten actief zijn)
->
Huisje wil wel getekend worden en vraagt aan de RenderEngine (apparte hoofd-draw class voor de engine, of eigenlijk gewoon Draw in een aparte .cs bestand ivm netjes houden) 
getekend te worden
->
Render Engine tekend Huisje op het beeldscherm.


Nu is dit het eerste, meeste logische/effectieve wat ik zo kan bedenken..

Maar op een of andere manier lijkt het niet uber efficient. Dus ik vroeg me af hoe jullie dit normaal oplossen.

Zelf heb ik voormezelf een logische map structuur gemaakt, deze leek me logisch en het lijkt me dan ook handig om vanuit die map structuur deze pijltjes te gaan tekenen (flowchart)

Mapstructuur:
code:
1
2
3
4
5
6
7
8
9
10
11
Content
           \Audio
           \Graphics
           \Objects
           \World
Gamesystem
           \AI
           \Input
           \Logic
Rendersystem
           \...


Ik zou graag jullie feedback willen over deze manier van zaken, en vinden jullie de map structuur logisch? Ik wil namelijk voor mijn eerste "combineer 100 tutorials en testjes met elkaar"-game wat structuur erin brengen zodat alles overzichtelijk blijft :).

~ Mijn prog blog!


  • .oisyn
  • Registratie: September 2000
  • Laatst online: 03:22

.oisyn

Moderator Devschuur®

Demotivational Speaker

Mapstructuur heeft natuurlijk an sich geen zak te maken met OO, dus als je je daar zorgen om maakt ben je denk ik een beetje verkeerd bezig :), Wat valt er trouwens onder "content", de data (.wav samples, .jpg textures, etc), of de code die nodig is om die dingen te managen? Indien dat eerste vind ik het een beetje raar dat content leeft op hetzelfde niveau als 'gamesystem' en 'rendersystem', terwijl die laatste twee toch echt code zijn. Indien het laatste, waarom valt rendersystem dan niet onder content?

Anyway, wat OO design betreft, waar je iig ver vandaan moet blijven is een class Huisje met een Draw() methode. Dat is compleet onpraktisch, want dat zorgt ervoor dat je voor elk type voorwerp in je wereld een vrijwel identieke Draw() method moet gaan zitten implementeren, namelijk eentje die gewoon zegt tegen de renderer dat een bepaald model getekend moet worden met een bepaalde transformatiematrix.

Een class Huisje is op zich prima, maar stop in dat soort classes alleen game logic code (wat wellicht leidt tot het feit dat een Huisje als class toch onzinnig is omdat een huis over het algemeen een statische entiteit is die geen logic nodig heeft ;)). Voor dingen als visibility determination en collision detection moet je meer denken aan een apart (onovererfbaar) object dat je instantieert, dat data vast houdt zoals een bounding volume en een transformatie matrix. Een klasse Entity oid, dat zich in een scenegraph of ander world management systeem bevindt. Dat systeem kan dan vervolgens gaan kijken wat allemaal zichtbaar is (en is daarbij compleet niet geïnteresseerd of iets een Huisje of een BadGuy is), en zal de zichtbare entities markeren om te tekenen.

Dat tekenen zou je dan met een Drawable interface kunnen doen die aan de entity hangt, die dan de Draw() methode bevat. Een mogelijke implementatie van zo'n Drawable is bijvoorbeeld eentje die de renderer een opdracht geeft om een skinned 3d model te tekenen, of eentje die een particle system modelleert. Voor je Huisje instantieer je dan gewoon een Entity in de wereld en een bijbehorende ModelDrawable met het model van dat huisje.

Ik vind je topicstart een beetje vaag en kan daarom niet goed hoogte krijgen van de problemen waar je nou tegenaan loopt, maar ik hoop dat dit je een beetje op weg helpt :)

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


  • roy-t
  • Registratie: Oktober 2004
  • Laatst online: 17-10 16:43
Dankje Oisyn, en ja je helpt me al zeker een eindje opweg.

Ik zal mezelf proberen te verduidelijken
eerste de makkelijke:
Hmm het feit dat ik mijn mapstructuur erbij zette stond eigenlijk los, meer van: ben ik logisch bezig.
In de map Content wilde ik inderdaad alle wav bestandjes e.d. zetten, dan in de map gamelogic de controllers hiervan en in rendersystem dan een drawfunctie, update functie en audio functie.

Dan nu het moeilijke,

Je had het toch aardig goed begrepen, wat ik vooral wil weten is wat nu een goede manier is om van "object vb huisje" komt tot dat je het op het scherm ziet.

Ik was namelijk, zoals je al aangaf, niet van plan om elk object (huisje) een eigen draw functie te geven.

Zoals jij het nu voorsteld snap ik:

game called update
->
scenemanager bekijkt zichtbare objecten, scenemanager itereerd door lijstje zichtbare objecten (bijvoorbeeld huisje)
->
Huisje wordt aangeboden aan drawfunctie, meegestuurd worden waarden als, schaal, positie, effect.
waarna de drawfunctie het object op het scherm pleurt :P

Dit was eigenlijk wat ik zocht (dus stiekem heb je toch het antwoord gegeven zover ik het snap)

Ik zat zelf ook al te denken aan een soort van scenemanager. Hmm, een soort van array van gameobjecten + een "is relevant" functie.. hmm

Wat ik me verder nog afvroeg is waar laat je de camera functie het beste. De WorldMatrix/ProjectionMatrix/Viewmatrix, zijn voor erg veel objecten gewoon gelijk. Mijn eerste gedachte was een apparte functie, maar misschien kan deze ook direct ineen draw class gezet worden... maar dat lijkt em dan weer slordig.

Hoewel je natuurlijk anders nogal wat heen en weer calls krijgt. Hmm...
*sorry voor mijn mogelijke warrigheid trouwens, tis laat :P*

~ Mijn prog blog!


  • .oisyn
  • Registratie: September 2000
  • Laatst online: 03:22

.oisyn

Moderator Devschuur®

Demotivational Speaker

Sowieso hebben objecten niets te maken met projection en view matrix, dat zijn idd camera dingen. Doorgaans hebben objecten alleen een world transform.

Een "camera" is niet echt een object op hetzelfde niveau als je huisje. 't Is gewoon een verzameling van wat gegevens - positie, orientatie (dit vormt je viewmatrix), fov, near plane, far plane (voor je projection matrix), die je op de renderer instelt.

[ Voor 4% gewijzigd door .oisyn op 28-10-2007 01:47 ]

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


  • roy-t
  • Registratie: Oktober 2004
  • Laatst online: 17-10 16:43
.oisyn schreef op zondag 28 oktober 2007 @ 01:46:
Sowieso hebben objecten niets te maken met projection en view matrix, dat zijn idd camera dingen. Doorgaans hebben objecten alleen een world transform.

Een "camera" is niet echt een object op hetzelfde niveau als je huisje. 't Is gewoon een verzameling van wat gegevens - positie, orientatie (dit vormt je viewmatrix), fov, near plane, far plane (voor je projection matrix), die je op de renderer instelt.
Nee klopt, maar ja het past wel in een algemenere beschouwing van mijn vraag.

Waar laat ik "alles" op een logische manier, en wat is de logische manier om deze met elkaar te verbinden. Eigenlijk zou ik voor mezelf een soort van flowchart willen tekenen. Ik zal even kijken en er even snel eentje maken zodat alles wat visueler wordt :)

Edit: flowchartje, alleen relevante gegevens gemodeleerd, nog even goed nagedacht en ik denk dat voor de scenemanager de camera moet komen, omdat van de gevens van de camera afgeleid kan worden welke objecten getekend moeten worden.
Afbeeldingslocatie: http://i23.tinypic.com/14uuydf.jpg

[ Voor 13% gewijzigd door roy-t op 28-10-2007 02:08 ]

~ Mijn prog blog!


  • .oisyn
  • Registratie: September 2000
  • Laatst online: 03:22

.oisyn

Moderator Devschuur®

Demotivational Speaker

Dat klopt idd wel aardig. De gameupdate zou ik als volgt onderverdelen:

Input / network -> Physics -> AI / gamelogic

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


  • roy-t
  • Registratie: Oktober 2004
  • Laatst online: 17-10 16:43
.oisyn schreef op zondag 28 oktober 2007 @ 02:05:
Dat klopt idd wel aardig. De gameupdate zou ik als volgt onderverdelen:

Input / network -> Physics -> AI / gamelogic
Goeiemorgen ;)
(iets met wtf.. net (5min geleden) stond je post er nog niet!)

Ah ok kijk, dan ben ik op het goede spoor. (flowcharts ftw!)

Gamelogic valt dan camera ook onder (even ter bevestiging, want het klinkt logisch) Op die manier gebeurt alles vrij logisch natuurlijk. :)

Nu zal dit topic voor anderen misschien compleet waardeloos lijken, je hebt me prachtig geholpen Oisyn, het is dan wel geen directe "hoe programeer ik goede physics 8)7 " of "ohnoes my integer doet niet wat ik wil" topic.

Maar een beetje structuur in een programma is ook erg belangrijk.

Hmm maar nu heb ik geen smoesjes meer.. nu moet ik wel beginnen met progselen :P *kruist vingers*

~ Mijn prog blog!


  • EfBe
  • Registratie: Januari 2000
  • Niet online
.oisyn schreef op zondag 28 oktober 2007 @ 01:19:
Mapstructuur heeft natuurlijk an sich geen zak te maken met OO, dus als je je daar zorgen om maakt ben je denk ik een beetje verkeerd bezig :), Wat valt er trouwens onder "content", de data (.wav samples, .jpg textures, etc), of de code die nodig is om die dingen te managen? Indien dat eerste vind ik het een beetje raar dat content leeft op hetzelfde niveau als 'gamesystem' en 'rendersystem', terwijl die laatste twee toch echt code zijn. Indien het laatste, waarom valt rendersystem dan niet onder content?

Anyway, wat OO design betreft, waar je iig ver vandaan moet blijven is een class Huisje met een Draw() methode. Dat is compleet onpraktisch, want dat zorgt ervoor dat je voor elk type voorwerp in je wereld een vrijwel identieke Draw() method moet gaan zitten implementeren, namelijk eentje die gewoon zegt tegen de renderer dat een bepaald model getekend moet worden met een bepaalde transformatiematrix.

Een class Huisje is op zich prima, maar stop in dat soort classes alleen game logic code (wat wellicht leidt tot het feit dat een Huisje als class toch onzinnig is omdat een huis over het algemeen een statische entiteit is die geen logic nodig heeft ;)). Voor dingen als visibility determination en collision detection moet je meer denken aan een apart (onovererfbaar) object dat je instantieert, dat data vast houdt zoals een bounding volume en een transformatie matrix. Een klasse Entity oid, dat zich in een scenegraph of ander world management systeem bevindt. Dat systeem kan dan vervolgens gaan kijken wat allemaal zichtbaar is (en is daarbij compleet niet geïnteresseerd of iets een Huisje of een BadGuy is), en zal de zichtbare entities markeren om te tekenen.

Dat tekenen zou je dan met een Drawable interface kunnen doen die aan de entity hangt, die dan de Draw() methode bevat. Een mogelijke implementatie van zo'n Drawable is bijvoorbeeld eentje die de renderer een opdracht geeft om een skinned 3d model te tekenen, of eentje die een particle system modelleert. Voor je Huisje instantieer je dan gewoon een Entity in de wereld en een bijbehorende ModelDrawable met het model van dat huisje.
Typisch visitor pattern dus: je renderer sjouwt de te tekenen objects af, en geeft zichzelf mee aan een method op die objects, en die objects die doen verder niets anders dan de render method aanroepen op de renderer die voor hun van belang is.

Creator of: LLBLGen Pro | Camera mods for games
Photography portfolio: https://fransbouma.com


  • roy-t
  • Registratie: Oktober 2004
  • Laatst online: 17-10 16:43
bedenk me ineens iets uit een stuk voorbeeld code van MS. Waarin ze ook nog een soort van "State Manager" hadden om te schakelen tussen: normale run, pauze, menu, cinematic etc.

Deze state manager zoals ik het snap zou dan meteen na (of in) Game.Update zitten. Opzich ook een logische plek hiervoor natuurlijk :)


Edit: Laat ik daar nog eens een tekeningetje van maken :P C&C welkom ofc! :)
Afbeeldingslocatie: http://i24.tinypic.com/js1qac.jpg
EfBe schreef op zondag 28 oktober 2007 @ 11:09:
[...]

Typisch visitor pattern dus: je renderer sjouwt de te tekenen objects af, en geeft zichzelf mee aan een method op die objects, en die objects die doen verder niets anders dan de render method aanroepen op de renderer die voor hun van belang is.
Ik heb even op wikipedia gekeken, maar Visitor Pattern, hoewel het redelijk abstract is klopt het deels. Het hele patroon in de Update Functie is inderdaad een Visitor Pattern, Maar het is niet "Huisje" wat de visitor is in dit geval, maar "state" (zie nieuwe flowchart onder), als state bij input komt reageerd deze daarop, idem voor Logic, AI, Camera etc...

Verder is er nog iets apparts, wat ik verkeerd getekend heb, Draw wordt appart gecalled door XNA, dus eigenlijk is er geen pijl van Scene Manager naar Draw.
Scene Manager zet elke update alles klaar (dit gebeurt geloof ik 60x per seconde) en Draw probeert zo vaak mogelijk te tekenen (dit gaat asynchroon) tot een max van 62 fps (standaard/vsync).

Dit maakt voor het design verder niet heel veel uit, behalve dan dat Scenemanager niet de functie draw called (als dit wel zou gebeuren dan zou je bij een lage fps ook ineens tragere kogels hebben bijvoorbeeld :P)

[ Voor 68% gewijzigd door roy-t op 28-10-2007 13:00 ]

~ Mijn prog blog!


  • EfBe
  • Registratie: Januari 2000
  • Niet online
Voor statemachines is het essentieel dat je state + event weergeeft in de transities. Dus wanneer je in state 'paused' zit, dat je aangeeft wanneer de state machine in een volgende toestand komt. Je tekening nu geeft dat niet aan en heeft eigenlijk niet zoveel toegevoegde waarde, want je ziet niet hoe je van cinematic naar normal gaat etc. en of dat uberhaupt plaatsvindt.

Creator of: LLBLGen Pro | Camera mods for games
Photography portfolio: https://fransbouma.com


  • roy-t
  • Registratie: Oktober 2004
  • Laatst online: 17-10 16:43
(was wat dingetjes aan het aanpassen terwijl jij poste)

En klopt wat je zegt, En nu zie ik ook ineens een probleem in mijn tekening.

Switchen van state kan natuurlijk op meerdere manieren gebeuren.

->toetsaanslag ESC
->Laden van nieuw level door binnen lopen einde level
->Dood gaan
->etc...

Voor deze functies zou ik statemanager zelf moeten modelleren. Maar 1 probleem is natuurlijk dat de input handler pas ver na statemanager komt.

Mogelijke oplossing:
-Apparte Inputhandler voor dat statemanager komt om "escape" af te handelen
-In de inputhandlerfunctie bij elke state "escape" een waarde laten switchen en dan de volgende update/draw pas switchen van state.

Maar voor bijvoorbeeld doodgaan, dit zit in de game logica..
-game logica zelf weer laten checken (slecht idee)
-game logica state weer laten veranderen.

Maar dan heb je het mogelijke probleem dat als er op escape wordt gedrukt terwijl je doodgaat de gamelogica de state overschrijft van de inputhandler...

Hmm dit geeft me wel wat stof tot nadenken zeg, beste oplossing lijkt met toch de laatste 2: gamelogic state laten veranderen en input state laten veranderen. Het moment escape + death (bijv) blijft wel een probleem, maar currentstate laten checken in gamelogic zou dat kunnen verhelpen. (escape=paused + menu, dus of de state dan op dood of niet staat boeit niet al te veel er gebeurt toch niks in de game.. tenzij er natuurlijk een andere kleur is als je dood bent die vliegt dan ineens op de achtergrond eraf... achter het menu zeg maar.. maar dan zou er een gecombineerde state moeten zijn... hmm hmm. *denker* )

[ Voor 21% gewijzigd door roy-t op 28-10-2007 13:13 ]

~ Mijn prog blog!


  • .oisyn
  • Registratie: September 2000
  • Laatst online: 03:22

.oisyn

Moderator Devschuur®

Demotivational Speaker

EfBe schreef op zondag 28 oktober 2007 @ 11:09:
[...]

Typisch visitor pattern dus: je renderer sjouwt de te tekenen objects af, en geeft zichzelf mee aan een method op die objects, en die objects die doen verder niets anders dan de render method aanroepen op de renderer die voor hun van belang is.
Niet helemaal, visible surface determination staat namelijk los van rendering, en ik denk dat het handig is als je die dingen dan ook in verschillende systemen modelleert. Een typische renderer is in feite maar een "dom" systeem dat alleen maar (high-level) tekenopdrachten afhandelt (zoals: render model X met bone matrices Y en lichten Z)

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


  • roy-t
  • Registratie: Oktober 2004
  • Laatst online: 17-10 16:43
.oisyn schreef op zondag 28 oktober 2007 @ 14:16:
[...]


Niet helemaal, visible surface determination staat namelijk los van rendering, en ik denk dat het handig is als je die dingen dan ook in verschillende systemen modelleert. Een typische renderer is in feite maar een "dom" systeem dat alleen maar (high-level) tekenopdrachten afhandelt (zoals: render model X met bone matrices Y en lichten Z)
Inderdaad, deze mag een appart systeem hebben, nog wel in het update gedeelte van het systeem denk ik, dus zeg maar na/in scenemanager.

Hmm nu zit ik nogsteeds met die states te klooien (goed uurtje over aan het na te denken) Ik denk dat ik maar gewoon is begin met wat in elkaar te flansen en dan kijk waar ik tegen aanloop, gewoon even een klein programmatje dat bijvoorbeeld een driehoekje rendered, alleen dan helemaal via dit patroon.

~ Mijn prog blog!


Verwijderd

Als ik het goed begrijp (ik heb een beetje met Ogre lopen prutsen) kan je een frame-listener op je scene zetten, waarmee je elk frame input afvangt. Als Esc dan een state-change veroorzaakt, kan dat dus elk frame gebeuren. Dan zeg je gewoon

code:
1
2
3
4
5
6
switch(keypressed)
{
    case MENU_KEY:
    mState.change(MENU);
    break;
}

Hoewel dat wel betekent dat je hele programma vastloopt als je renderer vastloopt/heel traag wordt.

woops, kickje

[ Voor 34% gewijzigd door Verwijderd op 08-11-2007 14:28 ]


  • roy-t
  • Registratie: Oktober 2004
  • Laatst online: 17-10 16:43
Maar wat is het voordeel daarvan boven het pakken van invoer tijdens de update cyclus? *(die standaard een x aantal per seconden polled. 30 of 60x dacht ik)

Voordeel hiervan is dat deze de render (die zo vaak als het maar kan tekent) niet in de weg zit. En deze zit standaard al in XNA, ik weet niet hoe dit in ogre zit though

~ Mijn prog blog!


Verwijderd

Mja, Ogre is slechts de grafische renderer. Ogre staat dan ook voor Object-oriented GRaphics Engine. In de voorbeelden bij de documentatie wordt de input in een framelistener afgehandeld. Daar doen ze dat denk ik om de focus op features van Ogre te houden. Input hoort eigenlijk überhaubt niet in een graphics engine thuis, maar je kan zo moeilijk dingen uitproberen zonder input. ;)

Input afhandelen in dezelfde loop als het renderen van een frame is niet eens zo erg. Als de renderer vast staat kan je toch niets meer zien, dus dan wordt het toch lastig om zinvolle input te geven. Het voordeel, als dat er al mag zijn, is dat je geen aparte thread hoeft te gebruiken voor de update cyclus. Maar je kan het meer als een nadeel zien, aangezien parallel-programmeren toch noodzakelijk wordt met de opkomst van multicore-CPUs/GPUs, en je de problemen die daarbij komen kijken sowieso zal tegenkomen.

  • roy-t
  • Registratie: Oktober 2004
  • Laatst online: 17-10 16:43
Ah ok,

maar toch zie ik updaten in de draw loop als een nadeel,

omdat de Draw loop voornamelijk op de grafische kaart is toegespits. Als deze vertraagd wordt wordt er pas later naar de videokaart geschreven die dan nog moet gaan rekenen.

Stel het kost 5ms om alle AI routines te doorlopen. en het kost normaal 16ms om een frame te renderen op basis van gegevens in een buffer (bijvoorbeeld object X staat op die positie).

Dan kost het zonder apparte update loop, 21ms = 48fps

Als je een apparte update loop hebt die fixed 30x per seconden update staat deze los van de videokaart, omdat (en helemaal op een dualcore) het update gedeelte 100% op de cpu zit, en het grafische gedeelte 70~90% op de videokaart zit.

In het meest optimale geval waar je 1 core + videokaart hebt voor rendering en 1 core voor de update kom je dan uit op een rendertijd van 16ms = 62,5fps.


Een ander nadeel is natuurlijk dat je bij een fixed step update loop (30x p/s) van bepaalde dingen uit kunt gaan. Maar dit is eigenlijk een nep-nadeel omdat er in XNA de variable "gametime" zit waarmee je de tijd tussen 2 update loops weet.

Deze moet je natuurlijk altijd meenemen in je berekeningen, anders hebben langzame pc's ineens kogels die minder snel vliegen :P

(dit is natuurlijk allemaal een stukje theorie omdat ik snap dat ogre het heeft omdat je anders gewoon je renderengine niet kunt testen :P )

[ Voor 5% gewijzigd door roy-t op 09-11-2007 20:23 ]

~ Mijn prog blog!


  • .oisyn
  • Registratie: September 2000
  • Laatst online: 03:22

.oisyn

Moderator Devschuur®

Demotivational Speaker

Ik vind het ook een beetje een raar design. Je bent niet van de renderer afhankelijk, en het is typisch juist de laatste stap in de hele loop. Je zou in feite al renderer::Update() aan kunnen roepen in een andere thread, en ondertussen alweer met de volgende iteratie kunnen beginnen (hoewel het wellicht handiger is om multithreading in de subsystems zelf af te laten handelen voor optimale parallellisatie, maar dat terzijde)

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.

Pagina: 1