Permalänk

Java problem

Hej!

Jag sitter och programmerar ett litet 2D-spel bara för skojs skull. Men har stött på ett problem som jag så gärna vill lösa.

Om jag tex. i en klass Character skapar ett objekt av en annan klass Inventory, kan jag då "få tag i" mitt character-objekt FRÅN inventoryobjektet.

Ex.
class Main
{
Character character;

Main()
{
character = new Character();
}
}

class Character
{
Inventory inventory;

Character()
{
inventory = new Inventory();
}
}

class Inventory
{
Inventory()
{
//här skulle jag vilja "komma åt" saker som finns i Main.
}
}

Kan alltså inventory-objektet på nåt sätt "komma åt" den kclass som skapade den (dvs. Character) så att det objektet sen kan komma åt den klass som i sin tur skapade character (dvs Main) ?

Säkert en himla dum fråga, men hade underlättat kodningen om det gick att "gå uppåt" i hierarkin.

Tack för svar!

Permalänk
Medlem

Det går om du skickar med en referens till förälderobjektet, men det är oftast inte vad du vill..

Objektorienterad programmering går i stort ut på att undvika såna här situationer. Objekten bör i så lång utsträckning som möjligt hantera sig själva utan information utifrån. När man börjar knyta samman klasser försvinner meningen med uppdelningen. Ta ett cykeldäck som exempel: däcket vet om ifall det är pumpat, vilken färg det har och dess lufttryck, men det vet inte om ifall det är monterat på en tandemcykel, sitter uppskruvat på en vägg eller studsar ner för en backe. Om däcket programmeras att vara beroende av denna information (var finns jag, vem använder mig?) omintetgörs samtidigt möjligheten att använda klassen i andra situationer. På samma sätt vet däckets ekrar om hur långa de är men inte om de sitter i ett cykelhjul.

edit: Vänder på frågan: varför vill Inventory komma åt information i Main?

Visa signatur

Kom-pa-TI-bilitet

Permalänk

Tack för svar!

Japp hänger med. Men har satt mig lite i en knipa här. Har skrivit ett litet spel där jag springer omkring med en gubbe och skjuter lite. Men har tagit det till nästa nivå och vill öppna en inventory.

Jag har skrivit inventoryn på det sättet att den öppnar en ny ruta. Problemet är bara att jag behöver ha reda på var den nuvarande rutan är placerad! Och den JPanel som jag ritar på har focus för att kunna känna av tangenbordet.

Såhär är det uppbyggt:

Main är huvudprogrammet. Main har en JFrame.
JFrame har en JPanel
JPanel har en World
World har en Karaktär
Karaktären har en Inventory
Inventoryn har en JFrame <------------här är problemet
JFrame har en JPanel

Jag vill kunna få tag i Main's JFrame för att sätta ut min location för Inventoryns JFrame.

Tack för svar!

EDIT: Samma sak gäller även när jag vill be min World att rita ut sin bakgrund. Då ritar jag ut en Image över hela panelen, men för att göra det måste jag veta paneles höjd/bredd. Så då måste jag skicka med Panelen när jag ber World att rita ut sig :S

Permalänk
Medlem

Är det verkligen karaktären som ska ha en inventory då? Informationen om att han har en apekatt och en handgranat ska kanske ligga i karaktär, men själva inventory-rutan tillhör väl GUI:t snarare än karaktären.

Fallet JPanel => World: Ritas något mer på panelen? Är det kanske så att World ÄR en JPanel, snarare än ett barn till JPanel (extend istället för inkapsling)

Om World innehåller abstrakt data som inte härrör till grafik så är det inte heller Worlds uppgift att rita om sig själv. Istället kan JPanelens paint-metod fråga World om dess storlek och sedan rita ut bakgrunden.

edit: när du MÅSTE knyta samman två objekt kan du göra det genom konstruktorn:

class Foo { Bar myBar; public Foo() { myBar = new Bar(this); } } class Bar { Foo myFoo; public Bar(Foo foo) { myFoo = foo; } }

Visa signatur

Kom-pa-TI-bilitet

Permalänk

Japp det låter mer vettigt att World faktiskt är en JPanel! I och med att det är det enda som ritas ut. Sedan kan ju World be sin bakgrund att rita ut sig och be sin karaktär att rita ut sig.

Då tar jag och skapar min inventory-frame med panel i Main, efter att jag skapat den "stora JFramen med Panel".

Usch vad jobbigt det blir när allt är så abstrakt haha "vad är vad?"

Permalänk
Medlem

Du kan ju lösa det med statiska variablar också antar jag? Iaf i fallet med x och y för inventoryn. Inte så snyggt dock.

Visa signatur
Permalänk

Men jag skulle kunna alltså kunna skicka med karaktären när jag skapar min Inventory i Main utan att bryta mot Objekt-orienterade tänket?

Karaktären lskapas i World. Efter jag skapat World kan jag skapa Inventory och kasta med karaktären som Inventoryn hör till?

Permalänk
Medlem

Ja det är krångligt faktiskt, körde fast där själv och håller bara på med teoretisk Javaprogrammering nu för tiden Ofta blir det att man stoppar in alldeles för mycket funktionalitet i en klass, den ska täcka hela behovet liksom..

Bara för att leka lite med tanken på hur en alternativ struktur ser ut har jag pillat ihop ett litet exempel som kan ses som brainstorming. I detta fallet skulle man kanske kunna dela upp allting ganska enkelt. Alla informationsobjekt som bara modellerar data heter som du redan har namngett det:

  • World

  • '-> Character

  • *'-> Inventory

En World har en Character som har ett Inventory.
Dessa innehåller information som namn, hälsa, innehåll. Rent datainnehåll, ingen information om hur dessa ska ritas ut.
Sen har du konkreta klasser som styr grafikritning:

  • GameWindow (extends JFrame)

  • '-> GamePanel (extends JPanel, overrides paintComponent())

  • * '-> InvetoryView (behöver inte vara en JComponent utan får Graphics g direkt från GamePanel och använder den för att rita direkt på GamePanel)

  • * '-> Minimap (samma som InventoryView. Har ingen egen modell utan hämtar information direkt från World pch Character (karaktärens position bestämmer vad som syns i minimapen))

GameWindow håller ihop GUI:t och skapar GamePanel som i sin tur skapar InventoryView och Minimap. Sedan måste naturligtvis InventoryView på något sätt få tillgång till datan i Inventory. Denna data kan vara publikt tillgänglig från World. På detta vis är GameWindow direkt beroende av World och dess barn, medan World är helt ovetandes om hur den presenteras.

Visa signatur

Kom-pa-TI-bilitet

Permalänk

Ja på det sättet får man upp en fin struktur. Men då skapar jag endast ett GameWindow i main. Detta GameWindow innehåller sedan World som innehåller karaktär som innehåller inventory etc.

Man skulle ju kunna få tag i inventoryn genom att i Karaktären ha en metod som heter Public Inventory getInventory() och i World ha en metod public World getWorld().

Om inventoryView är en JPanel kan den komma åt GameWindow genom getTopAncestor eller?

I så fall kan GameWindow i sin tur tillhandahålla sin World till InventoryView.

Men då är alltså InventoryView en JPanel eller JFrame

EDIT: Fast det blir nog allt för rörigt :S

Permalänk
Medlem

Egentligen behöver du faktiskt inte ha en InventoryView heller så länge som det bara finns en inventory. Så länge projektet inte blir jättestort så kan GamePanel hantera uppritningen av inventoriet direkt istället. Det är snarare om du ska ha flera olika inventories som du behöver en fristående view för den..

Visa signatur

Kom-pa-TI-bilitet

Permalänk

Ja det stämmer ju. Jag kommer bara ha en enda ruta som ploppar upp. Tanken är att det ska likna Link to the Past.

Men då har jag slutligen World som en egen klass. Dvs. att GamePanel säger åt World att rita ut sig? World säger i sin tur åt sina karaktärer att rita ut sig? Och karaktärerna säger åt sin flygande ammunition att rita ut sig?

Men jag måste nog ha InventoryView som ett Fönster med Panel. För jag vill kunna sätta fokus på det och bläddra runt med piltangenterna i Inventoryn. Och det kräver en Keylistener, som kräver en panel som kräver ett window.

Mycket svårt att få perfekt struktur!

EDIT: Kollisionshantering med väggar etc. kommer också ställa till problem, då karaktären eller världen inte vet vilka mått panelen har. Då måste panel skicka med sig själv vid updatering av världen och därmed karaktären

Permalänk
Medlem
Citat:

Ursprungligen inskrivet av Addeman_88
Men då har jag slutligen World som en egen klass. Dvs. att GamePanel säger åt World att rita ut sig? World säger i sin tur åt sina karaktärer att rita ut sig? Och karaktärerna säger åt sin flygande ammunition att rita ut sig?

Det beror som sagt på om Character har egna rutiner för att rita grafik. Är Character ett grafiskt objekt eller är det data om en spelkaraktär? Säg att du skulle vilja porta spelet till console, hur mycket skulle du då behöva skriva om? När du har datamodellen separerad är du fri att skapa views som presenterar datan hur du vill. Ponera följande..

class Boll { public String färg; public double studsFaktor; // ... }

Utan att krångla till det med metoder och dylikt. Denna kod kan ritas upp både grafiskt...

... extends JPanel { public void paintComponent(Graphics g) { String färg = minBoll.färg; if (färg.equals("röd")) then setColor(Color.RED); // ... }

och med text...

public void describeBall() { String studs; if (minBoll.studsFaktor > 0.5) { studs = "bra"; } else { studs = "dålig"; } System.out.println("Bollen du håller i din hand är " + minBoll.färg + " och har " + studs + " studs."); }

Om man istället hade låtit bollen ärva från JComponent och sålunda ha sin egen paintComponent-metod så hade man knutit koden till ett grafiskt gränssnitt. Koden hade förvisso kunnat ha färginformation och liknande men det hade kunnat resultera i bloat. Ett enkelt spel behöver egentligen inte mer än en klass, då slipper man ju problem med värdeåtkomst från olika delar av programmet. Om man vill skapa en klasstruktur är det viktigt att tänka igenom den noga.

Visa signatur

Kom-pa-TI-bilitet

Permalänk

Japp det är riktigt svårt att få en god struktur när jag delar upp klasserna.

Min tanke är såhär nu:

World innehåller karaktärer
Karaktärer har en inventory (inventoryn kan bestå av Boolean's tex. hasBow() etc.

Sedan har jag ett GameWindow. Detta har en GamePanel.
GamePaneln har en World.

När GamePaneln ritar om sig skickar den med sitt grafikobjekt till World.

public void paintComponent(Graphics g)
{
world.draw(g);
}

World ritar ut sin bakgrund och skickar sedan vidare g till sin karaktär

public void draw(Graphics g)
{
g.rdrawImage(bakgrund,0,0.............
karaktär.draw(g);
}

karaktären har en aktuell ikon som den ritar ut på sin specifika position.

Dvs GamePanel förser World med ett grafikobjekt som i sin tur förser karaktären med samma objekt. Allt fungerar jättebra.

**Problem**
Innuti World skall sedan kollisionshanteringen ta plats. Då behöver jag information om hur GamePanel ser ut. Detta vet inte World.

Jag vill sedan även kunna trycka på 'i' och få upp en inventory. GamePanel känner av knapptryckning på 'i'. Då ska ett nytt fönster öppnas som hämtar sin data från karaktärens inventory. Detta borde väl lösas enklast genom att GamePanel ber World att be Karaktären att be sin Inventory att öppna sig. Öppna sig = öppna sitt fönster och visa vad du innehåller.

När detta är gjort skall Inventoryfönstret få fokus, och tilldelas en panel samt keylistener. När knapptryckning åter sker på 'i' eller Enter skall fönstret stängas och fokus skall återföras till GamePanel.

**Problem**
GamePanel vet inte när InventoryPanel stängs. Inventory vet inte vem GamePanel är. Fokus kan inte sättas på GamePanel från InventoryPanel

Så detta summerar nog de största frågetecknena jag strider med nu

Tack för allt engagemang!

Permalänk
Medlem

För det första tycker jag inte att du ska ha hasXxx-metoder på din inventory eftersom föremål kan komma att läggas till eller tas bort, och då behöver du uppdatera din inventory-klass samt alla has-anrop som finns.

Vidare tycker jag fortfarande inte att karaktären ska rita sig själv, eller världen. Du har ännu inte berättat var du tänker spara positionsdata om karaktär/värld, så jag utgår ifrån att du har det i samma klasser? Då kan den problematik du beskriver uppstå, eftersom objekten ska rita sig själva men bara har tillgång till sin egen begränsade information.

Utifrån den samlade information du gett skulle jag haft en GamePanel-klass som hade tillgång till all nödvändig data och ritade datan därefter. Samma klass kan styra input och öppna nya frames. Man kan alltså dra det så långt att man slår samman kontroll och vy i en klass. För enkelhets skull kan vi kalla den Game (extends JPanel).

Då blir strukturen istället såhär:[list="1"]
[*]spelaren trycker höger piltangent för att flytta gubben i den riktningen;
[*]Game hämtar positionsdata från Character och jämför med World. Finns det någon vägg åt höger? Uppdatera Character-position om flytt är godkänd;
[*]Game ritar om sig. Detta är en stor funktion som hämtar data från alla aktörer och ritar dem.
[*]Efter ett tag trycker spelare på 'i'. Game skapar en InventoryPanel och ett nytt fönster, sätter InventoryPanel:n som contentPane och ger fokus till fönstret. Datan till InventoryPanel hämtas från karaktärens inventory (.getInventory(), returnerar en lista med GameItems eller dylikt). Samtidigt sätts en boolean i Game så att spelet pausas, kanske ritas en halvtransparent svart ruta över den andra grafiken så man får intrycket av att fönstret inte är aktivt?
[/list=1]
Problemet i detta läget är att Game blir en så kallad God-class: All information flödar genom Game, men det är kanske inte ett lika stort problem i detta läget..

Visa signatur

Kom-pa-TI-bilitet

Permalänk

Det låter som en himla bra struktur det där. Med väggar menade jag egentligen bara väggarna på fönstret så att säga. Men när jag kommer längre kommer jag även behöva jämföra med husväggar etc.

Men hur vet Game när InventoryPanel släcks? Dvs hur kan InventoryPanel sätta fokus tillbaka på Game? I och med att InventoryPanel inte har koll på vem som skapade denne.

Permalänk
Medlem

Det borde bara vara att sätta Game som InventoryPanels fönsters Action/WindowListener. Kommer close från inventoryWindow så stänger Game fönstret.

edit: WindowStateListener passar nog bra.

Visa signatur

Kom-pa-TI-bilitet

Permalänk

Hmm nu kommer vi in på saker jag inte tidigare gjort, får testa lite

Känns himla knäppt att det ska bli såhär krångligt! Objektoreinterat är ju till för att få bra struktur

Permalänk

Det jobbigaste är att man ständigt får ändra strukturen för att den ska vara kompatibel med alla klasser

Permalänk

Hmm..vad jag förstått av WindowsStateListener är att den används av ett Window, i mitt fall JFrame. Dock är Game inget Window, utan en Panel. Vilket leder till ännu en omstrukturering ?

Permalänk
Medlem

WindowStateListener är ett Interface. Du kan sätta den på vilken klass som helst; även class Dummy implements WindowStateChange { ... är legitimt. Det enda den gör är att försäkra att klassen kan lyssna till WindowStateChange-events. Du kan sålunda ha class Game extends JPanel implements WindowStateListener { .... När du skapar ditt inventory-fönster så knyter du det till game-klassen innan du lämnar över..

class Game ... { boolean inventoryIsOpen = false; ... JFrame inv = new InventoryFrame(myCharacter.getInventory()); inv.addWindowStateListener(this); // sätter Game som WindowStateListener till inventory-fönstret inv.pack(); inv.show(); inv.focus(); inventoryIsOpen = true; // påverkar hur Game ritar om sig/hanterar key inputs/focus.

Visa signatur

Kom-pa-TI-bilitet

Permalänk

åh ja det är klart! Tack så mycket för alla förslag, börjar få upp en fin struktur nu. Märkte dock innan att fokus sköter sig självt, antagligen beroende på vilket OS man kör. Lägger in focushantering i fall i fall!

Permalänk

Hej igen!

Nu har jag struktureat det lite mer så att Game är en så kallad God-class.

Känns dock som att det kommer bli rörigt att navigera sig i koden när spelet blir större.

Är det det som är främsta nackdelen med en God-class? Vad kan jag göra för att ta strukturen ännu ett steg åt det positiva? Börjar redan nu bli väldigt mycket kod i en och samma klass.

Nu är mina vanliga klasser mer informationskällor åt Game.

Character har tex. en position, en icon och en hastighet.
Game uppdaterar characters position och sätter aktuell icon på character.

Arrow har inte med character att göra, men har en position en hastighet och en icon. Game uppdaterar arrow och sätter dess startposition och icon.

etc.

Som sagt ovan blir det en HIMLA massa kod i Game, då de vanliga klasserna bara innehåller fakta.

Tack för svar!

Permalänk
Medlem

Precis, där har du problemet med logik centraliserad till en klass: tar du bort klassen finns det egentligen ingenting kvar av programmet. I detta fall skulle du ha kvar datamodeller utan funktion eller form.

Du berör en annan intressant punkt nu, nämligen att Character och Arrow har delade attribut. När man får två eller fler klasser som delar egenskaper kan det vara bra att skapa en superklass till dem som de båda ärver av.

Säg att du har ett antal klasser: Arrow, Rocket, Cloud, Dog. Alla ska röra sig. I din programkod har du följande:

rockets[] Rocket; arrows[] Arrow; clouds[] Cloud; dogs[] Dog; ... while (gameIsRunning == true) { for (int i=0; i<rockets.length; i++) { rockets[i].move(); } for (int i=0; i<arrows.length; i++) { arrows[i].move(); } // etc... }

Ovan nämnda klasser har alla en .move()-metod som kan flyttas till en [abstrakt] superklass, vi kallar den här för Actor. Du kan sedan omstrukturera såhär:

actors[] Actor; while(gameIsRunning == true) { for (int i=0; i<actors.length; i++) { actors[i].move(); // spelar ingen roll om actors[i] är en Rocket, Arrow eller Dog } }

En annan sak du kan göra för att strukturera ditt spel är att flytta bitar av objektrelaterad spelkod från game-klassen till nyskapade mindre klasser med samma målsättning. Exempelvis kan du skapa två nya klasser för din Character, en CharacterController och en CharacterView (observera att namnen här är riktigt dåliga). Utifrån sett hanterar bara din karaktär genom en av klasserna, Controllern. Du säger åt controllern att du vill flytta karaktären (charController.move()) och den i sin tur hämtar positionsdata från modellen och skickar till viewen så att den kan rita upp gubben. Det är precis så här Swing fungerar: Du har säkert skapat hundratals JFrames utan att tänka på att det i bakgrunden ligger en JFrameModel (obs, heter med största sannolikhet inte så). Olika Themes bestämmer sedan hur din JFrame ska se ut med ramar osv (detta är JFramens View).

Visa signatur

Kom-pa-TI-bilitet

Permalänk

Jaa jag satt och funderade på underklasser innan, dock var det svårt att klassificiera vilka objekt är av "samma typ". Men det du säger låter mycket logiskt. Börjar förstå att mina klasser endast skall innehålla beskrivande data om objekten, och låta Game ta hand om utritningen etc.

Så i stort sätt har jag datat i mina olika actors-klasser (rocket,dog,character etc.) och sedan har jag alla iconimages etc. i tex. charView och arrowView eller liknande?

När Game känner av tex. VK_LEFT kallar denne på charController com uppdaterar karaktären? och paincomponent kallar på charView osv.
Men hur är charView och charController bunden till character?

Kollisionshantering stannar i Game eller?

Tack för svar!=)

Permalänk
Medlem

Nä inte riktigt så. Views innehåller inte information som syns (bilder, färger osv) utan bestämmer HUR ett objekt visas.

I ditt fall kan man säga att Game har blivit både Controller och View för samtliga klasser då den sköter både dataflödet (hämta position, färg, ikon, har något krockat? Ta bort fiende) och bestämmer hur all slutgiltig information ska presenteras (ritas på en JPanel i detta fall).

Citat:

Ursprungligen inskrivet av Addeman_88
När Game känner av tex. VK_LEFT kallar denne på charController com uppdaterar karaktären? och paincomponent kallar på charView osv.
Men hur är charView och charController bunden till character?

Nej. När Game känner av VK_LEFT säger den åt Character (som är en kontroller) att den ska flytta sig till vänster. Controllern (Character) kontaktar sedan själv sin modell och hämtar/ändrar data, och säger åt sin View att rita om sig.

Game vet inte om att det finns en CharacterModel och CharacterView. Den bryr sig inte och ska inte behöva bry sig om detta.

Visa signatur

Kom-pa-TI-bilitet

Permalänk

Hmm..tror jag är med på tänket. Så alla iconer som finns med i spelet, skall de skötas helt av Game?

Om jag vill att character ska springa startas en timer som går igenom en LinkedList av ImageIcons och sätter aktuell bild på Character's icon. Det är så jag har det nu i alla fall.

Skall charView då egentligen bara ha en draw() som ritar ut objektet i fråga? Skall positionsdatat för Character ligga i charController? annars kan väl inte charControll "skicka tillbaka" datat till character

------Character------ (högsta nivå)

charView------- charControll (samma nivå)

Är den hierarkin fel tänkt?

Permalänk
Medlem
Citat:

Ursprungligen inskrivet av Addeman_88
Hmm..tror jag är med på tänket. Så alla iconer som finns med i spelet, skall de skötas helt av Game?

Om jag vill att character ska springa startas en timer som går igenom en LinkedList av ImageIcons och sätter aktuell bild på Character's icon. Det är så jag har det nu i alla fall.

Skall charView då egentligen bara ha en draw() som ritar ut objektet i fråga? Skall positionsdatat för Character ligga i charController? annars kan väl inte charControll "skicka tillbaka" datat till character

------Character------ (högsta nivå)

charView------- charControll (samma nivå)

Är den hierarkin fel tänkt?

Nej, du missuppfattar igen. Om du vill dela upp din nuvarande struktur mer så ska inte ikonerna skötas av Game. charController ska inte ha positionsdata, det ska modellen ha. Jag krånglade kanske till det för dig när jag började snacka om de här MVC-begreppen.

Det enklaste är att fortsätta ha Game som huvudklass i den utsträckning det nu går. Stöter du på fler problem går det alltid att omstrukturera. Om du vill läsa lite mer om Model-View-Controller-mönstret finns information på Wikipedia och google

Visa signatur

Kom-pa-TI-bilitet

Permalänk

Japp tror jag får plugga på det så jag förstår det men tack för all hjälp! nu vet jag i vilken riktning jag ska ta mig !

Permalänk

En sista fråga där bara, tror jag ska sätta mig med penna och papper och skriva upp asnvar för varje klass. Men innan jag gör det tänkte jag bara på en grej.

Alla objekt kan kolla sin egen position och uppdatera den etc, sätta sin icon utefter vad den "gör"? Tex. karaktären blir tillsagd att skjuta när knapptryck kommer på mellanslag. character.shoot(); Karaktären "sköter sig själv" och sätter igång en timer för att ändra sin icon.

JPanel skall endast sköta kollisionshantering av objekten. NU får du röra dig, NU får du INTE röra dig etc.

JPanel ber alla sina objekt att rita ut sig- character.draw(g); karaktären får själv avgöra var denne är och hur den skall rita ut sig? Den har vtå klassern Model och View som innehåller datat om var den befinner sig, vilken ikon den har etc. respektive hur datat presenteras på skärmen. Control är Character själv, den tar emot events av JPanel. Tex, Skjut, Spring Vänster, Hoppas etc.

Kan den synen på strukturen hålla? Tänkte bekräfta innan jag sätter mig och skissar, hade vart dumt om jag är helt väck och börjar skissa på nåt värdelöst haha

Permalänk
Medlem

Ja det låter rätt
JPanel ur ditt exempel ovan behöver inte vara en JPanel, men råkar vara det denna gång (fast det förstod du nog, ville förtydliga för säkerhets skull.)

Det är alltså Character's (i samverkan med dess modell/view) uppgift att rita sin animation och avgöra hur den ser ut, men den som säger till Character att animera/röra sig/göra något är alltså i detta exempel Game.

På så sätt kan du ha flera underklasser till Character som alla har move()-metoden men som animerar sig på olika vis.

Visa signatur

Kom-pa-TI-bilitet