Schulprojekt. Memory (Spiel) - App. Grundlegende Fragen

  • Antworten:11
Joscha D
  • Forum-Beiträge: 6

24.01.2013, 01:39:17 via Website

Hey,

ich bin derzeit auf einer BBS und hab mich bei meinem Abschlussprojekt für eine Android App entschieden. Dooferweise fällt es mir gar nicht so "leicht", wie erwartet (nicht das ich erwartet hätte es wäre leicht, aber so grundlegende Funktionen (Aufgabe ist auch nur die Einarbeitung) erhoffe ich mir doch schon.
Hab jetzt einige Tutorials durch und bin nun an der eigentlichen App dran. Bisher habe ich allerdings bloß einen Menü Screen mit 2 Buttons: Play und Exit. Play leitet weiter zum eigentlichen Spiel, das allerdings bisher noch nichts macht.

Meine Probleme:
Ich weiß nicht, wie ich die benötigte Anzahl an Bildern generiere, mit unterschiedlichen Namen.
Also ich habe eine Pic class (picture), von der ich lediglich Objekte anlege, die als Parameter bildurl und position übermitteln.
Wollte dann in einer for-Schleife für die AnzahlX*AnzahlY (Eben die Anzahl der Bilder) Pic Objekte erstellen und diese zeichnen:

1for (int i = 1; i < (anzahlx*anzahly)+1; i++){
2 Pic bild = new Pic(BitmapFactory.decodeResource(getResources(), R.drawable.pic1_1), i*size, 10);
3 bild.setPicName("bild"+i);
4 }
(Die Verwendeten Variablen sind natürlich gesetzt)

Allerdings wäre nun der eigentliche Objektname jedes mal bild. Nur eben der Eintrag "name", den ich mit setPicName festlege, ändert sich in Bild1, Bild2 etc, nur kann ich damit ja nichts ansprechen, oder ich weiß nicht wie?

-

Außerdem stelle ich mir die Frage, wie ich das Spiel an sich am besten lösen könnte. Die Bilden müssen ja zufällig platziert werden. Kann ich sagen rnd(0,24) mit der Bedingung, dass die letzte rnd Zahl nicht genommen wird? Das also jede Zahl mit einem rnd generiert wird, aber keine doppelt vorkommt?

Und kann mir vielleicht jemand eine schöne Erklärung zur Verwendung von Arrays verlinken? Finde da sehr wenig gutes. Denke mal ich müsste doch Arrays verwenden, oder? Z.B. für die Positionen? Bild[i] hat die position positionx[i], positiony[i]?
In meinem Kopf funktioniert das irgendwie, ..nur in der Praxis weiß ich nicht, wie ich es umsetzen kann.

Danke schon mal :)
Joscha

Antworten
Andreas Weichert
  • Forum-Beiträge: 287

25.01.2013, 11:51:52 via Website

Was ist rnd ?

Ganz klar ist mir noch nicht was Du vor hast. Zumindest lassen sich deine Fragen nicht nur mit einem einzigen Post beantworten.
Haben Deine Bilder den eingentlich eine Identität - heißt, dass eine spezielles Bild an einer bestimmten Stelle auf dem Bildschirm erscheinen soll, oder hast du einfach nur eine Menge von Bildern in beliebiger Reigenfolge?

— geändert am 25.01.2013, 11:53:34

Antworten
San Blarnoi
  • Forum-Beiträge: 2.545

25.01.2013, 13:03:01 via Website

Was ist rnd ?
Ich kenne das als Zufallszahlen-Generator aus Basic ;)


Das also jede Zahl mit einem rnd generiert wird, aber keine doppelt vorkommt?
Das geht mit keiner mir bekannten Random Funktion.
Ich würde das stattdessen mit einer ArrayList lösen, die Zufallszahl zwischen 0 und <listenlänge> wählen und das so indizierte Bild jeweils aus der Liste entnehmen; so bekommst du jedes Bildobjekt nur einmal selektiert.

Antworten
Joscha D
  • Forum-Beiträge: 6

25.01.2013, 19:04:28 via Website

Hey, ich bin mittlerweile schon etwas weiter gekommen:

Anfangs erstelle ich die Bilder als Pic objekte:
[code]
Pic Bilder[] = new Pic[AnzahlKarten];

// Objekte erstellen
for (int i = 0; i < AnzahlKarten; i++) {
Bilder[i] = new Pic(BitmapFactory.decodeResource(getResources(),
R.drawable.pic1_1));

if (i == 0 || i == 1) {
Bilder[i].setPicbmp(BitmapFactory.decodeResource(
getResources(), R.drawable.play));
} else if (i == 2 || i == 3) {
Bilder[i].setPicbmp(BitmapFactory.decodeResource(
getResources(), R.drawable.ic_launcher));
} else if (i == 4 || i == 5){
Bilder[i].setPicbmp(BitmapFactory.decodeResource(
getResources(), R.drawable.logo));
}
}
[/code]
(Als Beispiel habe ich hier manchen Objekten mal ein anderes Bild (setPicbmp) gegeben, um zu schauen ob es funktioniert.

Anschließend erstelle ich ein pos[] Array für die verschiedenen Positionen des Bildes. Zufällig und jede Zahl kommt nur 1x vor (mit Hilfe eines Freundes zustande gebracht):
[code] for (int i = 0; i < AnzahlKarten; i++) {
pos[i] = rnd.nextInt(AnzahlKarten);

while (positionset[pos[i]])
pos[i] = (pos[i] + 1) % AnzahlKarten;
positionset[pos[i]] = true;
}[/code]

In der getPosition funktion lege ich für jede position (Die Zahlen die zufällig generiert wurden) eine bestimmte Position fest:
1private void getPosition(int i) {
2
3 if (i <= 3) {
4 positiony = abstandy;
5 } else if (i > 3 && i < 8) {
6 i = i - 4;
7 positiony = 1 * (abstandy + size);
8 } else if (i > 7 && i < 12) {
9 i = i - 8;
10 positiony = 2 * (abstandy + size);
11 } else if (i > 11 && i < 16) {
12 i = i - 12;
13 positiony = 3 * (abstandy + size);
14 } else if (i > 15 && i < 20) {
15 i = i - 16;
16 positiony = 4 * (abstandy + size);
17 }
18
19 if (i == 0) {
20 positionx = abstandx * 2;
21 } else {
22 positionx = (i * size) + abstandx;
23 }
24 }

Und anschließend Zeichne ich diese dann
[code] for (int i = 0; i < AnzahlKarten; i++) {
getPosition(pos[i]);
canvas.drawBitmap(Bilder[i].getPicbmp(), positionx, positiony, null);
}[/code]

So. Das ganze funktioniert auch soweit. Hab jetzt bei jedem Start 20 zufällig platzierte Bilder.

Jetzt hapert es aber wieder. Erstmal weiß ich nicht, wie ich das anschließend realisieren könnte (also if klick auf bild dann if klick auf passendes gegenstück = sichtbar lassen, ansonsten wieder verstecken) und anscheinend verstehe ich auch nicht so recht, wie man das mit dem OnClick hinbekommt. Hab mir da was zusammengesucht und etwas rumgetestet. Z.B. mit
1public boolean isTouched(float x2, float y2) {
2 return x2 > x && x2 < x + width && y2 > y && y2 < y + height;
3 }
In der Pic class. Aber egal was ich in das onTouchevent schreibe, die App stürtzt bei jeder Berührung des Bildschirms einfach ab.
1public boolean onTouchEvent(MotionEvent event) {//
2 synchronized (getHolder()) {
3 Pic Bilder[] = new Pic[AnzahlKarten];
4
5 Bilder[1].setPicbmp(BitmapFactory.decodeResource(
6 getResources(), R.drawable.ic_launcher));
7 }
8 return super.onTouchEvent(event);
9}
Dachte damit würde ich irgendwo hinklicken können und Bild1 ändert sein Bild. Stürtzt aber wie gesagt einfach ab, weil es vermutlich auch totaler humbug ist? :/

Hoffe mir kann jmd auf die Sprünge helfen

Joscha

/e Wieso spinnt [code] ?

— geändert am 25.01.2013, 19:06:26

Antworten
Joscha D
  • Forum-Beiträge: 6

26.01.2013, 00:59:26 via Website

Jetzt habe ich außerdem nochmal die selbe Anzahl an Objekten erstellt, die ich einfach mal Hidden genannt habe, obwohl sie ja nicht versteckt nicht, sondern nur verstecken. Diese sehen jedes mal gleich aus und liegen über den eigentlichen Bildern. Bin mir sicher das geht auch besser, aber fürs erste wär das okay für mich.



Die Bilder mit "?" drauf sind Hidden[i] Objekte mit hidden.png als canvas und die sichtbaren sind die selben Objekte (Hidden[]), bloß mit einem anderen Bild das ich jetzt mal zu demonstrationszwecken erstellt habe (Weißer Rahmen, durchsichtig. Also das Bild unten drunter (Reddit) scheint durch).

So in etwa hätte ich das gerne auch später, bloß mit Funktionalität. Das mit dem onClick bekomme ich immer noch nicht hin. :/

/e Für die Hidden dinger könnte/sollte ich vll eine Liste nehmen? Dann kann ich daraus immer löschen und am Ende abfragen ob alle gelöscht sind = win?
Hab das aber nicht hinbekommen, da wurde bei mir am Ende nichts angezeigt (also keine hidden.png die drüber lagen, nichts wurde gezeichnet). Hatte dazu eine neue Klasse angelegt und ..ich weiß es gar nicht mehr genau, hab mich dabei aber "inspirieren" lassen.

— geändert am 26.01.2013, 01:06:26

Antworten
Andreas Weichert
  • Forum-Beiträge: 287

26.01.2013, 10:29:25 via Website

Bist ja schon gut weitergekomment - würde es aber doch ein wenig ander aufbauen.

Die Images könntes Du auch in den Asset-Ordner (bzw. Unterordner) als Dateien ablegen legen. Dann alle passenden Dateine suchen.
Dann müsstes Du nicht die einzelnen Resourcen aufzählen.

File folder = new File(BaseDir);
File[] list = folder.listFiles(new FilenameFilter() {
@Override
public boolean accept(File dir, String filename) {
return filename.endsWith("png"); // dein Filter
}
});
for (int i = 0; i < list.length; i++)
{
String fn = BaseDir + "/" + list[i].getName();
}


Die Bilder dann in ein Array laden, so wie Du dass ja auch schon gemacht hast. Das muß nur einmal bei Programmstart gemacht werden.

Ich würde dann die Spieleverwaltung von diesem Bilderspeicher trennen.

Ein Karte definieren

class Karte
{
int ImageNo; /// Verweise auf den Index in deinem Bildervektor
boolean Aufgedeckt;
int MatchingIndex; // wo ist die andere passenden Karte
}

Dann erzeugts Du ein Vector davon class Karten extends Vector<Karte>{}
Dieses Array initialisierts Du mit allen Karten, eine Karte jeweils 2 mal, somit ist ein Bild nur einmal im Speicher.

Diese Array mußt du mischen.
2 Zufallszahlen i, j im Indexbereich - dann die Karten im Stapel i, j vertauschen - das mehrmals durchführen. ca. 100.
(Oder natürlich irgedwas mit dem rnd mache - das ich nicht kennen)
Dann würde ich den MatchingIndex für jede Karteberechnen - d.h. wo die passende Karte sich im Stapel befindet

Damit hast Du alle Informationen zusammen und kann die Karten auf dem Screen darstellen. Ist mir im Moment nicht klar wie Du das machst.
Würde ein ImageButton oder nur Image verwenden - für diesen kann Du einen OnClickListener setzen.
In diesem speicherst Du alle nötigen Informationen ab (link auf die Karte) und muß dich nicht um Positionsberechnungen kümmern.

— geändert am 26.01.2013, 10:39:47

Antworten
Joscha D
  • Forum-Beiträge: 6

26.01.2013, 13:59:53 via Website

Danke schonmal.. Bin leider was Java angeht kompletter Neuling, hab bis vor einer Woche noch nie etwas damit gemacht und blicke daher nicht so recht durch bei dem was du vorschlägst.

Also den Pfad der Bilder könnte ich auch anders wählen, indem ich einfach quasi ein File Array erstelle, richtig? Die Bilder heißen auch alle bild0 bild1 bild2 etc, das trifft sich sicher gut.

Das mit den 2 zufälligen Werten habe ich jetzt nicht so ganz verstanden, ich google später mal um vielleicht etwas mehr ..zu verstehen.

Zurzeit stelle ich dir Bilder so dar:
[code] for (int i = 0; i < AnzahlKarten; i++) {
getPosition(pos[i]);
canvas.drawBitmap(Bilder[i].getPicbmp(), positionx, positiony, null);
}[/code]
Heißt ich zeichne einfach 20x ein Bild auf Bilder[i] mit der Position, die ich in getPosition() festlege (s.u.). Wie realisiere ich das denn mit Image oder sogar ImageButton? Die können doch, soweit ich weiß, nur in xml Dateien verwendet werden?

Joscha

Antworten
Andreas Weichert
  • Forum-Beiträge: 287

26.01.2013, 18:47:57 via Website

Hast Du schon Programmiererfahrung mit einer anderen Sprache?
Was ist dein Datentyp Pic ?

Wollte Dich nicht verwirren - vielleicht habe ich zuviel in Dein Projekt reigepfuscht. Mein Idee war nur, dass Du die Spielelogik von der Bildschirmdarstellung trennst. Du solltes eine Datenstruktur entwicklen, die den Zustand des Spieles vollständig speichert und die Spielelogik enthält
und dann Zeichnen.

Du must ja z.B. Wissen was für einen Zustand eine Karte hat.
Welches Bild, Welche andere Karte enthält das gleiche Bild, an welcher x,y Position, ist die karte umgedreht, Ist sie die 1. oder die 2. umgedrehte Karte umgedreht, etc. Eben die ganze Information die durch das Mischen und während des Spiele entsteht.
(Weiß ja nicht genau wie die Regeln deines Memorspieles sind.)
Dafür hatte ich die Klasse names Karte vorgeschlagen, Das Spiel besteht dann aus allen Karten was ein Vector von Karten ist.

class Karten extends Vector<Karte>
Das ist eben objektorientierte Programmierung .... Daher meine 1. Frage oben.

zum FileArray:
War nur ne Alternative die App dynamisch mit beliebigen Bildern arbeiten zu lassen und um nicht alle Resourcen aufzählen zu müssen. Wenns mit den Resourcen läuft ist auch ok.

Zum Absturz:
Gundsätzlich erstmal ins logcat schauen, da wird dir genau die Zeile angezeigt wo der Absturz passiert.

Pic Bilder[] = new Pic[AnzahlKarten];
Bilder[1]....... Hier müßte es abstürzen,
Das new erzeugt eine array von null Pointern! (Java ist kein C++, wo das gehen würde)
Die Arrayelemente muß Du mit new einzeln erzeugten




public boolean onTouchEvent(MotionEvent event) {//
2 synchronized (getHolder()) {
3 Pic Bilder[] = new Pic[AnzahlKarten];
4
5 Bilder[1].setPicbmp(BitmapFactory.decodeResource(
6 getResources(), R.drawable.ic_launcher));
7 }
8 return

Pic Bilder[] = new Pic[AnzahlKarten]; hier legst du ein

— geändert am 26.01.2013, 19:00:33

Antworten
Joscha D
  • Forum-Beiträge: 6

26.01.2013, 20:47:56 via Website

Hab bisher nur in PHP, LUA, HTML und CSS geschrieben, aber auch nichts großartiges.
Datentyp? Ehm, ..weiß nicht? Pic ist eine eigene Klasse, die kaum Inhalt enthält:

1import android.graphics.Bitmap;
2
3public class Pic {
4 //Main vars
5 private Bitmap bmp;
6 private int height, width;
7 private int x;
8 private int y;
9
10 //Konstruktor Pic
11 public Pic(Bitmap bmp){
12 this.bmp = bmp;
13 this.width = bmp.getWidth();
14 this.height = bmp.getHeight();
15 }
16 public int getPicWidth(){
17 return width;
18 }
19
20 public int getPicHeight(){
21 return height;
22 }
23
24 public void setPicbmp(Bitmap bmp){
25 this.bmp = bmp;
26 }
27
28 public Bitmap getPicbmp(){
29 return this.bmp;
30 }
31
32
33 public boolean isTouched(float x2, float y2) {
34 return x2 > x && x2 < x + width && y2 > y && y2 < y + height;
35 }
36}
Nur erstelle ich mit damit eben die Objekte für die Karten.

Ist ja kein Thema, hab ja jetzt extra darauf hingewiesen, dass ich erst seit kurzem damit arbeite. Eigentlich würde ich mir auch nicht so "hohe" Zielen für den Anfang setzen, aber es ist für die Schule und irgendwie wollte ich nicht mit einer Hello World App kommen, da kam mir (bzw einem Freund) die Idee für das Spiel Memory. Glaub ich hab mir da doch etwas viel vorgenommen.

Ja das mit dem Zustand speichern ist etwas was ich noch nicht ganz herausgefunden habe. Sollte es aber nicht reichen diese onPause Funktionen anzupassen? Bisher ist es wirklich so, das wenn ich die App "minimiere" und wieder öffne, alle Bilder verschwunden sind.

Das mit dem Zustand der Karte wollte ich etwas dämlich lösen: Ich ändere beim klick das Bild in blank.png (dadurch wird das darunter sichtbar) und frag eben mit getPicbmp() ab ob es blank ist. ... Ohje ich glaub ich muss quasi von vorne Anfangen :(

Vector habe ich leider auch noch nie gehört, muss ich mich reinlesen.

FileArray: Ja es läuft schon zZ, aber wenn es in kompakt auch möglich ist, wäre mir das natürlich immer lieber.

Absturz: Hab auch schon anders versucht Bilder1 anzusprechen, das scheint auch nicht zu funktionieren? Und im Logcat hab ich bisher nie so wirklich eine Zeile oder sowas gefunden..

Mein Kopf ist heute eh etwas voll, kann mich da grad schlecht drauf konzentrieren. Muss ich morgen, oder später noch mal dran. Ich mache immer 2mm Fortschritte innerhalb von 5 stunden, das ist ein bisschen ärgerlich :)

Antworten
Andreas Weichert
  • Forum-Beiträge: 287

27.01.2013, 12:49:39 via Website

Das debuggen solltes Du als erstes hin bekommen - ohne dieses Hilfe ist es schwierig - geerade für einen Anfänger.

Im LogCat sollte jeder Fehler durch eine rote Zeile dargestellt werden. Doppel-click darauf und die Bug-Zeile wird dir angezeigt.
Dazu muß im Manifest die App als debuggable gesetzt sein und die App im Debugmodus starten.
Würde im LogCat-Fenster ein Filter auf dein Packetname setzen, damit nicht zu viele andere Meldungen von Android dargestellt werden.

Sonst eben eine Haltepunkt setzen und Zeile für Zeile durch-steppen.

"Das mit dem Zustand der Karte wollte ich etwas dämlich lösen: Ich ändere beim klick das Bild in blank.png (dadurch wird das darunter sichtbar) und frag eben mit getPicbmp() ab ob es blank ist. ... Ohje ich glaub ich muss quasi von vorne Anfangen"

Wie gesagt eine Datenstruktur entwickeln die den Zustand einer Karte bzw. des gesamten Spieles enthält. (Verdeckt, Offen, RausAusdemSpiel, Welche Karte 1. Click, 2. Click-Karte, etc.) Den Zustand einer Karte über ein pointer auf ein Bild ist eine wenig schräg (eine Karte hat mind. 3. Zustände).
Alles mit boolean, Enum, integer kodieren und im Spieleverlauf durch die Click-Aktionen und den Regeln die Daten verändern.
Aus der aktuellen Datenstruktur deinen Screen zeichnen.

— geändert am 27.01.2013, 12:50:44

Antworten
Joscha D
  • Forum-Beiträge: 6

29.01.2013, 11:17:33 via Website

Für das debuggen bin ich wohl irgendwie zu blöd, das gibt zwar Fehler aus, aber nur in welcher Datei - nicht welche Zeile. Und die Halter die ich setze, werden auch einfach ignoriert :/

Hab jetzt versucht das ganze umzuschreiben und auch mal, es ganz anders zu lösen:

Umgeschrieben:
In die Pic.class (bzw Karten.class) habe ich ein
1private boolean aufgedeckt;
geschrieben + setter und getter.
Hat zwar noch keine Verwendung, wird aber hoffentlich noch kommen.

Außerdem habe ich versucht die Position dort zu setzen mit der selben Lösung wie auch vorher:
1public Pic(Bitmap bmp, int x){
2 setAufgedeckt(false);
3 this.bmp = bmp;
4
5
6 // Zufällige Position
7 this.pos[x] = rnd.nextInt(AnzahlKarten);
8
9 while (positionset[pos[x]])
10 this.pos[x] = (pos[x] + 1) % AnzahlKarten;
11 positionset[pos[x]] = true;
12
13
14 // getPosition
15 getPosition(pos[x]);
16 }

Allerdings wird positionset wohl nicht global gespeichert, heißt die Positionen kommen mehrfach vor. Ich muss die aber meines Wissens nach in dieser Klasse genau festlegen, weil ich sonst nicht bei onTouch einen X oder Y wert abfragen kann..?
Zurzeit kann ich das mit getX() und getY(), die Position bestimme ich wieder mit der getPosition() Funktion.


Den anderen Weg den ich dachte zu verwenden ist das mit GridView zu lösen:
Da lassen sich ja die Bilder einfach in einer Tabelle quasi einordnen und diese sind dann auch anklickbar (onClickListener), was ja durchaus praktisch ist.
Allerdings bekomme ich es nicht hin, die Sachen zu "würfeln".

Hab Collections.shuffle gefunden, was allerdings nur für Listen zu verwenden ist und ein Arrays.asList(..) löst leider nur Fehler aus.
Eine andere Möglichkeit die ich gefunden habe, wäre die folgende:
[code] private Integer[] BilderArray = {
R.drawable.bild0, R.drawable.bild1,
R.drawable.bild3, R.drawable.bild4,
R.drawable.bild5, R.drawable.bild6,
R.drawable.bild7, R.drawable.bild8,
R.drawable.bild9,

R.drawable.bild0, R.drawable.bild1,
R.drawable.bild3, R.drawable.bild4,
R.drawable.bild5, R.drawable.bild6,
R.drawable.bild7, R.drawable.bild8,
R.drawable.bild9
};


Random rnd = new Random(); <-

for (int i = 0; i < BilderArray.length; i++) {
int change = i + rnd.nextInt(BilderArray.length - i);
int helper = BilderArray[i];
BilderArray[i] = BilderArray[change];
BilderArray[change] = helper;
} <-[/code]

Allerdings spuckt der mir an den markierten (<-) Problemen mir nicht erklärbare Fehler aus, wenn ich das ganze NICHT in ein

1public static void onCreate( String args[] ){
2..
3}

(o.ä.) stecke. Mit gehts aber dann auch nicht, die App stürtzt wieder nur ab :/

Hab derweil mal ein bisschen was anderen getestet, also kein Spiel. Nur für den Fall der Fälle, das ich das Spiel nicht zuende bringe.

Hat jemand noch einen Tipp/eine Idee?

Joscha

— geändert am 29.01.2013, 11:18:30

Antworten
Andreas Weichert
  • Forum-Beiträge: 287

29.01.2013, 15:01:58 via Website

Wenn die App nicht beim Haltpunkt hält ist das Debuggen nicht aktiviert.
Ist Manifest debuggable auf true gesetzt ? ja/nein
Hast Du die App im Debugmodus gestartet? ja/nein

Ansonsten geb ich auf dir bei der strukturiert Programmierung zu helfen - es schein sinnlos zu sein.
Ich bin raus....

Antworten