Toon posts:

[Java] Vloeiende bewegingen maken

Pagina: 1
Acties:

Verwijderd

Topicstarter
Ik ben bezig om een Snake-applet te schrijven, waarbij ik graag wil dat de slang langs een rechte lijn kronkelt, i.p.v. gewoon kaarsrecht gaat zoals op je mobieltje.
Mijn idee was om verschillende plaatjes te maken: één van een kronkel naar boven langs een rechte lijn, één van een kronkel naar beneden langs een rechte lijn, vier hoeken voor als de slang van richting verandert wordt, en natuurlijk een kop en een staart.
Door de kronkels naar boven en naar beneden af te wisselen, krijg je een kronkeleffect.

Ik vraag me echter een aantal dingen af:

- Gaat het laden van zo'n plaatje(zeg 1 a 2kb) met de MediaTracker wel snel genoeg? Moet je steeds dat plaatje laden m.b.v. addImage(), en verwijderen met removeImage(), of is er een efficientere manier? Gaat het beeld dan niet 'flikkeren'?
- In BASIC had je zoiets als DIM, waarmee dit soort dingen redelijk gingen. Bestaat er niet zoiets in Java?
- Kun je aangeven op welke positie een plaatje wordt geladen in je JPanel? Ik kan dit in de Javadoc van MediaTracker nergens vinden.
- Om een goede vloeiende beweging te krijgen zul je waarschijnlijk veel meer plaatjes moeten toevoegen, bijv. van kronkels met 'halve' hoogte. Denken jullie dat het haalbaar is om op deze wijze een goede vloeiende beweging te krijgen, of wordt het dan óf te schokkerig óf te traag?
- Mocht het wel lukken: hoe krijg je het voor elkaar dat de slang op iedere pc even snel gaat (onafhankelijk van de snelheid van de pc)? Is het gebruik van sleep() dan toereikend of zijn hier betere methoden voor?

Zo het was een heel verhaal, ik hoop dat jullie een beetje snappen wat ik bedoel ;)
Hopelijk zijn er mensen die hier meer ervaring mee hebben :)

PS: Voor de zekerheid hier even mijn source tot nu toe: het [url="http://www11.brinkster.com/minne/Snake.java"]Snake-object[/url] en het [url="http://www11.brinkster.com/minne/KoKoSnake.java"]hoofdprogramma[/url]
PPS: De directe link werkt niet, maar "Doel opslaan als..." wel ;)

  • Janoz
  • Registratie: Oktober 2000
  • Laatst online: 28-08 12:00

Janoz

Moderator Devschuur®

!litemod

Je kunt die plaatjes toch gewoon in een var opslaan?

Ken Thompson's famous line from V6 UNIX is equaly applicable to this post:
'You are not expected to understand this'


Verwijderd

Topicstarter
Ja tuurlijk, maar dan moet je nog wel iedere keer addImage() en removeImage() doen. Bovendien kun je (volgens mij) geen positie in het panel opgeven bij deze methods...

Verwijderd

- Gaat het laden van zo'n plaatje(zeg 1 a 2kb) met de MediaTracker wel snel genoeg? Moet je steeds dat plaatje laden m.b.v. addImage(), en verwijderen met removeImage(), of is er een efficientere manier? Gaat het beeld dan niet 'flikkeren'?
De mediatracker is niet verantwoordelijk voor het laden van een plaatje. Je kunt plaatjes aan de mediatracker toevoegen en dan een thread laten wachten tot alle plaatjes geladen zijn. Op deze manier kun je het programma de plaatjes laten 'preloaden' en pas verder gaan als ze allemaal geladen zijn. Zodra de plaatjes in Java zijn ingeladen kun je ze meestal gewoon gebruiken zonder dat je rekening hoeft te houden met laadtijden etc.
- Kun je aangeven op welke positie een plaatje wordt geladen in je JPanel? Ik kan dit in de Javadoc van MediaTracker nergens vinden.
De mediatracker is ook niet verantwoordelijk voor het plaatsen van een plaatje op een panel of het tekenen ervan. Dit moet je zelf doen. Het makkelijkste is om de paint(Graphics) method van je panel te overschrijven, het mooiste is om een datastructuur van PlayField en BodyParts te maken en deze op het panael te laten tekenen. Het ligt er maar aan hoeveel tijd je eraan wilt besteden :)
- Om een goede vloeiende beweging te krijgen zul je waarschijnlijk veel meer plaatjes moeten toevoegen, bijv. van kronkels met 'halve' hoogte. Denken jullie dat het haalbaar is om op deze wijze een goede vloeiende beweging te krijgen, of wordt het dan óf te schokkerig óf te traag?
Zolang de plaatjes niet te groot zijn zal dit denk ik geen probleem zijn.
- Mocht het wel lukken: hoe krijg je het voor elkaar dat de slang op iedere pc even snel gaat (onafhankelijk van de snelheid van de pc)? Is het gebruik van sleep() dan toereikend of zijn hier betere methoden voor?
Sleep werkt op zich goed genoeg, maar het is schijnbaar mooier om een swing Timer te gebruiken. Ik gebruik meestal gewoon Thread.currentThread().sleep(..) om het op te lossen (volgens mij werkt dit ook op de kaffee VM, terwijl de swing Timer hier niet beschikbaar is, correct me if I'm wrong).

[edit]
Typos

  • Janoz
  • Registratie: Oktober 2000
  • Laatst online: 28-08 12:00

Janoz

Moderator Devschuur®

!litemod

Je kunt waarschijnlijk beter niet met een panel werken, maar rechtstreeks op de graphics tekenen. Om flikkeren te voorkomen kun (eigenlijk moet) je gebruik maken van double buffering. Je tekent dan je scherm op een image en kopieerd deze vervolgens op het scherm. Door deze buffer steeds leeg te maken hoef je je niet druk te maken over het weer weghalen van plaatjes.

Het tijdsprobleem kun je oplossen door gebruik te maken van een thread. Vraag voor het begin de timestamp op en vlak voordat je de sleep aanroept weer. Hieruit kun je de tijd bepalen die nodig is geweest voor je algoritme en laat je je thread de rest van de tijd die je hebt ingesteld per frame slapen. Wat je ook kunt doen is deze tijd gebruiken als stapgroote. Hierdoor loopt het op alle computers even snel, maar loopt het op een snellere computer vloeiender.

Ken Thompson's famous line from V6 UNIX is equaly applicable to this post:
'You are not expected to understand this'


Verwijderd

Je kunt waarschijnlijk beter niet met een panel werken, maar rechtstreeks op de graphics tekenen.
Hoe wou je dat doen (geen flame, gewoon interesse)? Je moet toch op een panel (of in ieder geval op een component) tekenen om aan die graphics te komen.
Om flikkeren te voorkomen kun (eigenlijk moet) je gebruik maken van double buffering.
Bij mijn weten worden JComponents vanzelf met double buffering gerendered, dus als je op een JPanel/JComponent je slang tekent hoef je je hier niet druk om te maken.

Verwijderd

Nouja dat gaat bijna vanzelf, moet ff bij je constructor dit zetten ->

public class MyPanel extends JPanel{
public MyPanel(bla bla bla){
super(true); <--
// Rest van je progje

Hierbij geef je aan dat ie gedoublebufferd moet worden... Zelf ben ik niet zo gek van het gebruik van swing componenten in spelletjes, willen soms nogal rare dingen met je graphics gaan doen ;(

Greetings Morloth

  • Janoz
  • Registratie: Oktober 2000
  • Laatst online: 28-08 12:00

Janoz

Moderator Devschuur®

!litemod

Op dinsdag 23 juli 2002 12:59 schreef websjwans het volgende:

Hoe wou je dat doen (geen flame, gewoon interesse)? Je moet toch op een panel (of in ieder geval op een component) tekenen om aan die graphics te komen.
Gewoon in de paint(g) van Frame implementeren (wat inderdaad wel een subclasse is van component). Vervolgens kun je gewoon Graphics of Graphics2D gebruiken.

Zeker voor een game heb je een andere benadering tov graphics nodig. Een game is vaak niet event driven (ok mijnenveger natuurlijk wel :), maar ik hoop dat je begrijpt wat ik bedoel). Het uiterlijk moet constant worden geupdate. Dit vraagt dus ook meer voor een 'render lus' ipv 'actie -> reactie'.
Bij mijn weten worden JComponents vanzelf met double buffering gerendered, dus als je op een JPanel/JComponent je slang tekent hoef je je hier niet druk om te maken.
Als je zelf het scherm leeg gaat maken en stuk voor stuk onderdelen gaat toevoegen heb je het liefst dat de buffer pas wordt gewisseld waneer je helemaal klaar bent. Ik weet niet precies wat voor mogelijkheden JPanel/JComponent heeft, maar het zou me niet verbasen als die hier geen rekening mee houden.

Ken Thompson's famous line from V6 UNIX is equaly applicable to this post:
'You are not expected to understand this'


Verwijderd

Topicstarter
Stomme fout van mij met die MediaTracker, je moet natuurlijk gewooon met Graphics2D.drawImage() tekenen |:(
Dat idee met die timestamp opvragen lijt me ook wel goed werken, ik ga dat maar es proberen :)

Alleen dat met die double buffering snap ik niet precies:
Je tekent dan je scherm op een image en kopieerd deze vervolgens op het scherm. Door deze buffer steeds leeg te maken hoef je je niet druk te maken over het weer weghalen van plaatjes.
Nouja dat gaat bijna vanzelf, moet ff bij je constructor dit zetten ->

// Stukkie code

Hierbij geef je aan dat ie gedoublebufferd moet worden.
Ok... dus ik roep die super(true) aan. Als ik nu dus een repaint() ga doen (die overgeschreven paintComponent() uit JPanel), dan wordt dus het scherm eerst achter het vorige scherm opgebouwd, en dan in één keer getoond, en heb je dus geen flikkering? Of begrijp ik het nu niet goed :?

  • Stephan Oudmaijer
  • Registratie: Oktober 2000
  • Laatst online: 16-08-2023
zoeken is ook een kunst hoor sjees!

[url="http://developer.java.sun.com/developer/technicalArticles/Interviews/DoubleBuffering/d-buffer.txt"]http://developer.java.sun.com/developer/technicalArticles/Interviews/DoubleBuffering/d-buffer.txt[/url]

  • Janoz
  • Registratie: Oktober 2000
  • Laatst online: 28-08 12:00

Janoz

Moderator Devschuur®

!litemod

Ik zal wel ff een voorbeeldje geven. Hieronder mijn paint methode in een pong applet dat ik een keer gemaakt heb:
code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
    public void paint(Graphics g){
      offScreenG.setColor(backgroundColor);
      offScreenG.fillRect(0,0,appletWidth,appletHeight); // fill/empty screen

      offScreenG.setColor(foregroundColor);

      //draw ball
      
      offScreenG.drawImage(ballImage, ballX-ballRadius, ballY-ballRadius,this);
      //offScreenG.fillOval(ballX-ballRadius, ballY-ballRadius,
      //              ballRadius*2, ballRadius*2);


      //draw bars
      
      offScreenG.drawImage(barImage, leftBaseline-batWidth, upperBound-batWidth, this);
      //offScreenG.fillRect(leftBaseline-batWidth, upperBound-batWidth, 
      //              rightBaseline-leftBaseline+(2*batWidth), batWidth); 
      
      offScreenG.drawImage(barImage, leftBaseline-batWidth, lowerBound, this);
      //offScreenG.fillRect(leftBaseline-batWidth, lowerBound, 
      //              rightBaseline-leftBaseline+(2*batWidth), batWidth); 
      
      //draw bats
      
      offScreenG.drawImage(batImage, leftBaseline-batWidth, leftBatPosition, this);
      //offScreenG.fillRect(leftBaseline-batWidth, leftBatPosition, 
      //              batWidth, batLength);
      
      offScreenG.drawImage(batImage, rightBaseline, rightBatPosition, this);
      //offScreenG.fillRect(rightBaseline, rightBatPosition,
      //              batWidth, batLength);
      
      //draw text
      
      offScreenG.setFont(smallFont);
      offScreenG.drawString(Integer.toString(scoreLeft),
                    leftBaseline,
                    upperBound-(2*batWidth));
      offScreenG.drawString(Integer.toString(scoreRight),
                    rightBaseline-offScreenG.getFontMetrics().stringWidth(Integer.toString(scoreRight)),
                    upperBound-(2*batWidth));
      offScreenG.setColor(emphisizeColor);

      if (waitForClick) 
        offScreenG.drawString(waitForClickString,
                        (appletWidth/2)-(offScreenG.getFontMetrics().stringWidth(waitForClickString)/2),
                        (appletHeight/2) + smallFontSize);
      offScreenG.setFont(bigFont);
      offScreenG.drawString(msg,
                    (appletWidth/2)-(offScreenG.getFontMetrics().stringWidth(msg)/2),
                    (appletHeight/2));

      //Copy to screen
      g.drawImage(offScreenImg,0,0,this);
    }

Deze code wordt door een thread om de xx miliseconde aangeroepen door redraw uit te voeren. Ikzelf heb eigenlijk nog nooit echt met swing gewerkt, dus het zou best kunnen dat dit in standaard al in JPanel zit.

Dilemma: Mooie code layout of mooie Got layout?

Ken Thompson's famous line from V6 UNIX is equaly applicable to this post:
'You are not expected to understand this'

Pagina: 1