Jag tänkte pussla ihop något enkelt för att hantera indata, dvs i första hand musklick och tangentbord samt SDL_QUIT.
Från SDL till lägsta nivån kommer events som SDL_Event. Jag tänkte införa en statisk klass Input på lägsta nivån med en metod Input::getEvent(unsigned long timeOut) som hanterar SDL_Event och skickar upp ett objekt av typen PrimitiveEvent till mittennivån (i nuläget liktydigt med main-filen). PrimitiveEvent kan vara MouseEvent, KeyboardEvent eller QuitEvent.
I ett senare skede processar mittennivån dessa PrimitiveEvent och skickar upp GameEvent till översta nivån. Ett GameEvent kan vara att spelaren interagerar med ett objekt eller något annat som kräver respons på manusnivå. Men det blir väl som sagt i ett senare skede.
Vad tror du Ulf, krockar det här med något som du hade tänkt? Jag ser inte riktigt hur du har tänkt styra animationen. Apropå det... du hade väl tänkt kommentera lite bättre..?
/Jerker
On Mon, Mar 17, 2003 at 09:38:20PM +0100, Jerker Hammarberg wrote:
I ett senare skede processar mittennivån dessa PrimitiveEvent och skickar upp GameEvent till översta nivån. Ett GameEvent kan vara att spelaren interagerar med ett objekt eller något annat som kräver respons på manusnivå. Men det blir väl som sagt i ett senare skede.
Vad tror du Ulf, krockar det här med något som du hade tänkt?
Näe då, det ser bra ut. På det här viset blir det ju också en mer formell uppdelning av det olika nivåerna, vilket är bra.
Jag ser inte riktigt hur du har tänkt styra animationen.
Allt är inte klart än :) Just nu så är enda möjligheten att slänga på en ny animation på spriten, men egentligen ska animationerna ju uppdateras automatiskt. Däremot behöver vi något sätt att signalera till spriten att animationen är slut. Kanske en virtuell metod hos Sprite?
Här måste vi nog tänka lite. Nu ska jag tjata om Airstrike igen, för där fanns samma problem:
Sprites har även en hastighet, och det är Display-klassens uppgift att flytta dem. Det är också Display-klassen som flyttar fram animationer. Det är ju inte självklart att det ska vara så, kanske ska man ha en separat "motor" som flyttar och uppdaterar sprites, och Display ritar bara upp spritesen där de råkar vara. Detta känns som en bra lösning för Odd.
Vad händer då när en animation tar slut eller liknade? I a.s. loopar animationen som default, men om man vill kan man sätta en pekare till en funktion som ska köras när en viss frame nås. Detta blir väl en virtuell metod (hos AnimationFrame eller Sprite?) i C++.
En annan sak jag har i a.s. är att man kan skicka meddelanden mellan alla sprites i spelet; man kan till exempel berätta att de ska svänga vänster, eller ge dem skada. Detta kanske inte behövs på samma sätt i Odd eftersom vi inte har någon "fysik", utan skriptar alla händelser.
En sak som vi kommer behöva är i alla fall en händelsekö, med en viss tid för varje händelse. Detta kanske kan kopplas ihop med GameEvent?
Apropå det... du hade väl tänkt kommentera lite bättre..?
Dokumentera bättre, ja. Ville bara få fram något fort i söndags. Ska vi köra på någon särskild stil, för Doxygen eller så?
Ulf
Allt är inte klart än :) Just nu så är enda möjligheten att slänga på en ny animation på spriten, men egentligen ska animationerna ju uppdateras automatiskt. Däremot behöver vi något sätt att signalera till spriten att animationen är slut. Kanske en virtuell metod hos Sprite?
Fattar inte riktigt... menar du att varje reell animation ska vara en subklass och implementera den virtuella metoden? Låter jobbigt... hellre funktionsobjekt så, eller någon annan callback-grej. Men personligen är jag mer för någon form av "uppifrån-styrning", vet inte riktigt hur jag ska kalla det... i princip, när en primitiv rutin inte vet längre vad den ska göra, så ska den returnera. Jag tycker det känns naturligare att tänka så. Men det beror förstås på hur kontrollen över animationerna sköts - jag har ännu inte förstått hur du har tänkt och då blir det svårt att hitta på en passande arkitektur.
Det är ju inte självklart att det ska vara så, kanske ska man ha en separat "motor" som flyttar och uppdaterar sprites, och Display ritar bara upp spritesen där de råkar vara. Detta känns som en bra lösning för Odd.
Mm, det är nog ett exempel på det jag menar med "uppifrån-styrning". Jag röstar för detta förslag!
Så här skulle jag ha tänkt:
På mittenivån finns en klass Animation:
class Animation { public: long getNextUpdateTime(); [kanske virtual] bool isDone(); [kanske virtual] void doUpdate(); };
Den har också fält med all information som behövs för att styra animationen, tex om den loopar eller inte och hur den förflyttar sig, samt förstås pekare till sin Sprite och sin AnimationFrame. För att starta en animation så lägger man till den i en allmän lista över animationer.
På mittennivån ligger också "the main loop" om man säger så. I varje iteration bläddrar den igenom den allmänna animationslistan och kollar för varje animation om det är dags för en uppdatering (getNextUpdateTime() < currentTime). I så fall körs doUpdate(). Om isDone() är true så måste mainloopen vidta någon styråtgärd, vilket kan innebära att returnera till toppnivån ifall det krävs åtgärder på manusnivå. I vilket fall ska animeringsobjektet tas bort ur listan, och förmodligen ersättas med en annan animation för samma Sprite.
Detta hänger ihop med timeOut-parametern på Input::getEvent(). I och med att vi bläddrar igenom alla animationer kan ve se hur lång tid det är tills någon måste uppdateras. Detta värde används då som timeOut, så att getEvent() returnerar lagom tills det är dags att uppdatera.
Men som sagt, ta detta bara som information om hur jag tänker... du är mer erfaren på sånt här så det är bättre att du bygger det som du tror blir bra.
En annan sak jag har i a.s. är att man kan skicka meddelanden mellan alla sprites i spelet; man kan till exempel berätta att de ska svänga vänster, eller ge dem skada. Detta kanske inte behövs på samma sätt i Odd eftersom vi inte har någon "fysik", utan skriptar alla händelser.
Jag tror inte heller att det behövs. I Airstrike fanns det ju anledning att göra Sprites intelligenta, här är de dumma i den meningen att de styrs uppifrån.
En sak som vi kommer behöva är i alla fall en händelsekö, med en viss tid för varje händelse. Detta kanske kan kopplas ihop med GameEvent?
Mm, kanske. GameEvent tänkte jag skulle förmedla handlingar från användaren från mittennivån till toppnivån, typ "Odd undersöker dörr", och sådant behöver ju ingen tidsangivelse. Men kanske måste GameEvent vara något mer. Kan du ge exempel på händelser i en sådan kö?
Dokumentera bättre, ja. Ville bara få fram något fort i söndags. Ska vi köra på någon särskild stil, för Doxygen eller så?
Kör nån som du kan och som inte är för bökig, tycker jag. Jag är bara van vid JavaDoc men den funkar väl inte på C++ antar jag.
/Jerker
On Tue, Mar 18, 2003 at 03:02:09PM +0100, Jerker Hammarberg wrote:
[callbacks från animationer]
Fattar inte riktigt... menar du att varje reell animation ska vara en subklass och implementera den virtuella metoden? Låter jobbigt...
Ja, det är jobbigt.
hellre funktionsobjekt så, eller någon annan callback-grej. Men personligen är jag mer för någon form av "uppifrån-styrning", vet inte riktigt hur jag ska kalla det... i princip, när en primitiv rutin inte vet längre vad den ska göra, så ska den returnera.
Kanske vi kan ha någon form av kooperativ multitasking för "primitiva rutiner", typ:
class Task { public: Task() { nextRun = -1; } virtual ~Task() {}
// Performs a part of the task, return true if // there is more to be done, else false. virtual bool run(void) = 0;
// Gives the absolute time when this task wants to run, or -1 if // "as soon as possible" int nextRunTime() { return nextRun };
private: int nextRun; };
Vi har också en kö med Task's, och kollar en gång per frame vilka som behöver köras. Det blir ju lite extra krångel med att ärva Task, fast jag kan inte komma på något bra alternativ. Man vill ju ofta ha med sig lite kontext i olika "processer", och då behövs ändå en klass.
Jag tycker det känns naturligare att tänka så. Men det beror förstås på hur kontrollen över animationerna sköts - jag har ännu inte förstått hur du har tänkt och då blir det svårt att hitta på en passande arkitektur.
Jag har tänkt, fast inte kommit på någon ultimat lösning, så du får gärna ha åsikter!
[..]
Så här skulle jag ha tänkt:
På mittenivån finns en klass Animation:
class Animation { public: long getNextUpdateTime(); [kanske virtual] bool isDone(); [kanske virtual] void doUpdate(); };
Tusan, nu ser jag att du redan har uppfunnit "Task".
Den har också fält med all information som behövs för att styra animationen, tex om den loopar eller inte och hur den förflyttar sig, samt förstås pekare till sin Sprite och sin AnimationFrame. För att starta en animation så lägger man till den i en allmän lista över animationer.
Precis. Kanske vill vi ha andra typer av saker (därav "Task") som kör parallellt, med det kanske bara är att krångla till det?
På mittennivån ligger också "the main loop" om man säger så. I varje iteration bläddrar den igenom den allmänna animationslistan och kollar för varje animation om det är dags för en uppdatering (getNextUpdateTime() < currentTime).
Inte för att jag tror att det behövs, men en kö sorterad på getNextUpdateTime() blir lite lite snabbare.
[..]
Om isDone() är true så måste mainloopen vidta någon styråtgärd, vilket kan innebära att returnera till toppnivån ifall det krävs åtgärder på manusnivå.
Hur kommer egentligen manuset att lagras? Som noder i en stor graf? Jag funderar på det där med "returnera till toppnivån".. Ofta vill man ju "returnera" till olika ställen i manuset beroende på vad som hände, och eftersom utgången kan vara väldigt öppen i en del fall.
Något sånt här?
class GameEvent { int type(); //egentligen en enum av olika typer av händelser. };
Om man antar en funktion
GameEvent LetOddWalkAround() //Let the user walk around and return when something interesting happens
Så kan man skriva en scen i manuset som
GameEvent event = LetODDWalkAround();
switch (event->type()) { case EVENT_PICKUP: .. case EVENT_USE: .. case EVENT_TALKTO: .. };
Jag gillar inte riktigt det här, fast fördelen är att man kan hålla all kod för en viss scen på samma ställe. Annars blir den kanske utspridd över en massa klasser. Har du någon idé på hur man kan förbättra det här?
I vilket fall ska animeringsobjektet tas bort ur listan, och förmodligen ersättas med en annan animation för samma Sprite.
Mm. Detta ska ju i högst möjliga mån gå automatiskt.
Detta hänger ihop med timeOut-parametern på Input::getEvent(). I och med att vi bläddrar igenom alla animationer kan ve se hur lång tid det är tills någon måste uppdateras. Detta värde används då som timeOut, så att getEvent() returnerar lagom tills det är dags att uppdatera.
Så tänker jag mig också.
[..]
En sak som vi kommer behöva är i alla fall en händelsekö, med en viss tid för varje händelse. Detta kanske kan kopplas ihop med GameEvent?
Mm, kanske. GameEvent tänkte jag skulle förmedla handlingar från användaren från mittennivån till toppnivån, typ "Odd undersöker dörr", och sådant behöver ju ingen tidsangivelse. Men kanske måste GameEvent vara något mer. Kan du ge exempel på händelser i en sådan kö?
Diverse animationer som sker spontant, en fågel som kvittrar eller så. Ingenting som påverkar själva handlingen dock. Om man har en datorstyrd karaktär kanske det också kan vara bra för att få den att gå omkring spontant.
Dokumentera bättre, ja. Ville bara få fram något fort i söndags. Ska vi köra på någon särskild stil, för Doxygen eller så?
Kör nån som du kan och som inte är för bökig, tycker jag. Jag är bara van vid JavaDoc men den funkar väl inte på C++ antar jag.
Jag kan ingen speciell, men jag ska läsa på om doxygen.
Ulf
Precis. Kanske vill vi ha andra typer av saker (därav "Task") som kör parallellt, med det kanske bara är att krångla till det?
Nejdå, du har så rätt! Animation kan ärva av Task kanske.
Hur kommer egentligen manuset att lagras? Som noder i en stor graf?
...
Något sånt här?
...
GameEvent event = LetODDWalkAround();
switch (event->type()) { case EVENT_PICKUP: .. case EVENT_USE: .. case EVENT_TALKTO: .. };
Jag gillar inte riktigt det här, fast fördelen är att man kan hålla all kod för en viss scen på samma ställe. Annars blir den kanske utspridd över en massa klasser. Har du någon idé på hur man kan förbättra det här?
Precis så hade jag tänkt ja! Vad är det du inte gillar? Jag kan mycket väl ha missat något... men tanken är precis som du säger, att all kod för en scen är samlad, så att man kan "programmera" manuset direkt i C++.
I vilket fall ska animeringsobjektet tas bort ur listan, och förmodligen ersättas med en
annan
animation för samma Sprite.
Mm. Detta ska ju i högst möjliga mån gå automatiskt.
Hur menar du? Allt som inte användaren behöver göra är ju automatiskt... du kanske menar att Animation-objektet är tillräckligt smart för att själv länka sig till en ny animation när den gamla är klar? Helt OK för mig!
Diverse animationer som sker spontant, en fågel som kvittrar eller så. Ingenting som påverkar själva handlingen dock. Om man har en datorstyrd karaktär kanske det också kan vara bra för att få den att gå omkring spontant.
Aha, så menar du... ja det där har jag faktiskt inte tänkt på över huvud taget. Men jag tolkar det som något annat än de GameEvent som jag har pratat om. Det är nog ett dåligt namn, UserEvent borde det heta. GameEvent passar bättre för det du nämner. "Dina" GameEvents är väl något som initeras baserat på manuset, och som poppas på mittennivån?
/Jerker, also extremely bored at work
Hej igen.
[..]
GameEvent event = LetODDWalkAround();
switch (event->type()) { case EVENT_PICKUP: .. case EVENT_USE: .. case EVENT_TALKTO: .. };
[..]
Precis så hade jag tänkt ja! Vad är det du inte gillar? Jag kan mycket väl ha missat något... men tanken är precis som du säger, att all kod för en scen är samlad, så att man kan "programmera" manuset direkt i C++.
Det är ju inte så "objektorienterat", fast det gör kanske inget.
Som ovan blir det ungefär:
case EVENT_USE: if (event.useWhichObject() == "pinnen" && event.useOnObject() == "ögat") print("aj!") .. massa if-satser ..
Alternativet är väl att skriva kod i varje "fysiskt" objekt i spelet.
Pinnen::useOn(GameObject obj) { if (obj == "ögat") print("aj"); }
Det här var ju väldigt simpelt, men ofta har ju "användningar" bieffekter som påverkar på manusnivå, men det kanske räcker med att ha ett gäng tillståndsvariabler för varje scen. Eftersom varje objekt i spelet i princip är unikt så blir nog metod 1 bäst, som du säger. Med en klass för varje objekt blir det ju också en himla mass onödig kod.
Jag använde strängar i exemplet för att slippa hitta på en ny klass, men det kanske inte är så dumt att ha en dynamisk namnrymd för objekt i spelet?
I vilket fall ska animeringsobjektet tas bort ur listan, och förmodligen ersättas med en annan animation för samma Sprite.
Mm. Detta ska ju i högst möjliga mån gå automatiskt.
Hur menar du? Allt som inte användaren behöver göra är ju automatiskt... du kanske menar att Animation-objektet är tillräckligt smart för att själv länka sig till en ny animation när den gamla är klar? Helt OK för mig!
Jag menar att animationsbyte inte ska komma upp till en högre nivå än vad som krävs för att bestämma nästa animation, men det är ju självklart märker jag nu när jag skriver ut det.
[events]
"Dina" GameEvents är väl något som initeras baserat på manuset, och som poppas på mittennivån?
Japp.
Ulf
Alternativet är väl att skriva kod i varje "fysiskt" objekt i spelet.
Pinnen::useOn(GameObject obj) { if (obj == "ögat") print("aj"); }
OK, ja egentligen är det väl inget som hindrar att man gör så heller, men som du säger tror jag det blir rätt jobbigt. Fast kanske kan man definiera generella objekt på högsta nivå:
class Item // Pinnar, BBQ-set mm class Character // Utskrivningsförrätare, Elvis [kanske också] class Scene
som innehåller så mycket generell funktionalitet att alla objekt kan definieras av parametrar hos de här klasserna. Men det kanske är mer för en fullskalig version tillsammans med ett skriptspråk... Hur som helst, beteendet från mittennivån att skicka upp UserEvent borde vara kompatibelt även med detta.
Jag använde strängar i exemplet för att slippa hitta på en ny klass, men det kanske inte är så dumt att ha en dynamisk namnrymd för objekt i
spelet?
Visst! De borde matcha den konvention som vi redan har, tex "cliff.bbqset".
OK, vi verkar vara rörande överens! Tänker du hacka något ikväll? Annars sätter jag igång, och satsar på att fixa till en gående Odd. Jag tror nämligen jag har kommit på ett sätt att fuska fram transparenta png-bilder.
/Jerker
On Tue, Mar 18, 2003 at 06:55:45PM +0100, Jerker Hammarberg wrote:
Fast kanske kan man definiera generella objekt på högsta nivå:
class Item // Pinnar, BBQ-set mm class Character // Utskrivningsförrätare, Elvis [kanske också] class Scene
som innehåller så mycket generell funktionalitet att alla objekt kan definieras av parametrar hos de här klasserna.
Ja, absolut, det tycker jag också. Om inte annat för att hantera animering och grafik.
[..]
det kanske inte är så dumt att ha en dynamisk namnrymd för objekt i
spelet?
Visst! De borde matcha den konvention som vi redan har, tex "cliff.bbqset".
Ja, man kan säkert använd samma kod både för delar av resurshantering och för objekthantering i "manuskoden".
OK, vi verkar vara rörande överens! Tänker du hacka något ikväll?
Nej, det blir det nog inte.
Annars sätter jag igång, och satsar på att fixa till en gående Odd. Jag tror nämligen jag har kommit på ett sätt att fuska fram transparenta png-bilder.
Coolt!
En sista pusselbit tror jag (kanske) behövs: Garbage collection.
Anledningen är att när man har events som ligger och väntar på att få referera till objekt, som då kanske inte finns längre, så får man problem.
Exempel:
1) Skapa en "Task" för att hålla igång en animation av ett objekt. 2) I en helt annan del av koden dödas objektet, till exempel för att det har användts till något. Här kan man inte deleta objektet, eftersom Task:en håller en pekare till det, och kommer försöka ändra på objektets animation.
... Du förstår problemet, man vet aldrig vem som minns ett visst objekt.
I a.s. har jag en referensräknare i varje sprite, och man måste använda funktioner
sprite_acquire(sprite_t *s); //håller en pekare till s sprite_isvalid(sprite_t *s); //kollar om sprites fortfarande lever, måste användas innan man petar på s sprite_release(sprite_t *s); //släpper referensen. Om refcount <= 0 så frigör minne etc
I C++ kanske man kan använda såna där smarta pekare för att göra automatisk gc, har du erfarenhet av det?
Ulf
Ja, absolut, det tycker jag också. Om inte annat för att hantera animering och grafik.
Japp, och sedan kan man kanske bygga ut efter behov.
En sista pusselbit tror jag (kanske) behövs: Garbage collection.
...
- Skapa en "Task" för att hålla igång en animation av ett objekt.
- I en helt annan del av koden dödas objektet, till exempel för att det har användts till något. Här kan man inte deleta objektet, eftersom
OK, jag ser inte riktigt behovet ännu men det kanske kommer. I ditt specifika exempel ser jag inte varför objektet dödas när det har använts.
I C++ kanske man kan använda såna där smarta pekare för att göra automatisk gc, har du erfarenhet av det?
Ja, auto_ptr fixar nog biffen tror jag!
/Jerker
En sista pusselbit tror jag (kanske) behövs: Garbage collection.
Nu har jag ändrat mig igen, det var ett dum förslag. Vi kommer ju aldrig skapa mer än ett begränsat antal objekt per scen, och de kan ju deletas när man byter scen, så behovet finns nog inte som du säger.
Ulf
Jäsiken...
Jag börjar inse att dina AnimationFrame och Sprite inte alls är gjorda för styrning uppifrån så som jag tänkte. Du hade förstås tänkt att Sprites skulle sköta (flytta och animera) sig själva utan styrning uppifrån.
Vi gör så här... det är bättre att du får bygga klart animeringen så som du hade tänkt, och så fortsätter vi därifrån. Jag checkar in vad jag har gjort ifall du vill sno något, men jag inser att det mesta av vad jag gjort på Animation får vi kasta (eller föra över till Sprite).
Jag checkar också in lite png-bilder, och jag har bytt namn på bg.png till cliff.background.png .
/Jerker
Jag börjar inse att dina AnimationFrame och Sprite inte alls är gjorda för styrning uppifrån så som jag tänkte. Du hade förstås tänkt att Sprites skulle sköta (flytta och animera) sig själva utan styrning uppifrån.
Nej! Jag tänkte som du (tror jag). Spritesen håller reda på om de ha blivit ändrade, men det är bara för att Display ska kunna optimera sin omritning lite bättre. Jag tänkte att "Sprite" inte ska ärvas, i alla fall inte ännu.
Vi gör så här... det är bättre att du får bygga klart animeringen så som du hade tänkt, och så fortsätter vi därifrån. Jag checkar in vad jag har gjort ifall du vill sno något, men jag inser att det mesta av vad jag gjort på Animation får vi kasta (eller föra över till Sprite).
Jag ska ta en titt.
Jag checkar också in lite png-bilder, och jag har bytt namn på bg.png till cliff.background.png .
Bra, det borde jag gjort från början förstås.
Ulf
Jag är ganska nyvaken fortfarande..
On Wed, Mar 19, 2003 at 10:29:14AM +0100, Ulf Ekström wrote:
Nej! Jag tänkte som du (tror jag). Spritesen håller reda på om de ha blivit ändrade, men det är bara för att Display ska kunna optimera sin omritning lite bättre. Jag tänkte att "Sprite" inte ska ärvas, i alla fall inte ännu.
Det är klart Sprite kan ärvas, till exempel till en klass Character, jag vet inte varför jag skrev sådär. Men jag tror fortfarande att de ska styras "uppifrån"
Ulf
Jag ska ta en titt.
OK, toppen! Själv kommer jag nog inte att programmera något ikväll, och i morgon åker jag bort sex dagar. Så du kan hacka i lugn och ro ett tag framöver, det är nog bra!
Jag checkar också in lite png-bilder, och jag har bytt namn på bg.png
till
cliff.background.png .
Bra, det borde jag gjort från början förstås.
Mm, jag tror dessutom vi borde flytta dem till den katalog där de ska vara "på rikigt", förmodligen i en katalog som heter "resources" eller bara "files". Jag börjar för övrigt tveka över om CVS är det bästa sättet att lagra grafik och ljud, det är rätt bökigt och tar en förbannad massa plats eftersom gamla versioner aldrig tas bort. Kanske bara ha en katalog på webben?
/Jerker
Jag börjar för övrigt tveka över om CVS är det bästa sättet att lagra grafik och ljud, det är rätt bökigt och tar en förbannad massa plats eftersom gamla versioner aldrig tas bort. Kanske bara ha en katalog på webben?
Går det att ordna på något bra sätt så att alla kan ladda upp och ner saker? En ftp där vi har konton vore bra, går det att fixa på lysator tror du?
Ulf
Går det att ordna på något bra sätt så att alla kan ladda upp och ner saker? En ftp där vi har konton vore bra, går det att fixa på lysator tror du?
Nej tyvärr, jag har redan frågat och Lysator godkänner inte konton för projekt. Man måste ha personligt konto och då måste man vara medlem, kostar 192 pix. Men då är det inte ftp utan ssh som gäller.
Men jag tänkte så att de stora sakerna - bilder och musik - kommer huvudsakligen från mig (eller via mig), och då kan ju jag lägga upp dem i tex http://www.lysator.liu.se/odd/resources . Fast kanske det är smidigare med CVS ändå, så slipper man hålla på och plocka hem filer över webben varje gång det kommer något nytt. Det blir nog rätt jobbigt det också när det börjar bli många filer.
/Jerker