Vorlage
Processing-Projekt für Spiele mit mehreren Ansichten (Screens): gameskeleton.zip
Schwierigkeitsstufe 1: Tic Tac Toe
Schwierigkeitsstufe 2: Snake
Schwierigkeitsstufe 3: Sokoban
Code zu Tic Tac Toe
- TicTacToe.pde
final int NONE = 0; final int CROSS = 1; final int CIRCLE = 2; final int SPLASHSCREEN = 0; final int PLAYING = 1; final int GAMEOVER = 2; final int[][] WINNING_COMBINATIONS = { { 0, 1, 2 }, // first row { 3, 4, 5 }, // second row { 6, 7, 8 }, // third row { 0, 3, 6 }, // first column { 1, 4, 7 }, // second column { 2, 5, 8 }, // third column { 0, 4, 8 }, // first diagonal { 2, 4, 6 } // second diagonal }; int[] game = { NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE }; int currentPlayer = NONE; int[] winnerIndices = null; boolean gameIsDraw = false; String highScoreText = ""; int gameState = SPLASHSCREEN; int size; int xOffset; int yOffset; int cellSize; PFont headingFont; PFont textFont; void setup() { size(500, 500); headingFont = createFont("Liberation Sans Bold", 32); textFont = createFont("Liberation Sans", 16); size = min(width, height); xOffset = (width - size) / 2; yOffset = (height - size) / 2; cellSize = size / 3; rectMode(CENTER); ellipseMode(CENTER); textAlign(CENTER, CENTER); } void draw() { switch (gameState) { case SPLASHSCREEN: background(#000000); fill(#FFFFFF); textFont(headingFont); text("TikTakToe", width / 2, height / 4); textFont(textFont); text("A two-player game\n\nPress any key or click anywhere to start playing\n\n© 2022 Christian Weber", width / 2, height / 2); break; case PLAYING: background(#CCCCCC); checkWinner(); drawWinnerIndices(); drawBoard(); drawPlayerMarkers(); break; case GAMEOVER: background(#CCCCCC); drawWinnerIndices(); drawBoard(); drawPlayerMarkers(); fill(#77000000); noStroke(); rect(xOffset, yOffset, 6 * cellSize, 6 * cellSize); fill(#FFFFFF); textFont(headingFont); text("Game Over", width / 2, height / 4); textFont(textFont); String text = "The player using " + (currentPlayer == CROSS ? "crosses" : "circles") + " has won the game."; if (gameIsDraw) { text = "The game is a draw."; } text(text + "\n\nPress any key or click anywhere to play again." + highScoreText, width / 2, height / 2); break; } } void drawBoard() { stroke(#000000); strokeWeight(5); line(xOffset + cellSize, yOffset, xOffset + cellSize, height - yOffset); line(xOffset + 2*cellSize, yOffset, xOffset + 2*cellSize, height - yOffset); line(xOffset, yOffset + cellSize, width - xOffset, yOffset + cellSize); line(xOffset, yOffset + 2*cellSize, width - xOffset, yOffset + 2*cellSize); } void drawWinnerIndices() { if (winnerIndices == null || winnerIndices.length <= 0) return; for (int i = 0; i < winnerIndices.length; i++) { int x = winnerIndices[i] % 3; int y = winnerIndices[i] / 3; noStroke(); fill(#AACCEE); rect(xOffset + x * cellSize + cellSize / 2, yOffset + y * cellSize + cellSize / 2, cellSize, cellSize); } } void drawPlayerMarkers() { for (int i = 0; i < game.length; i++) { int x = i % 3; int y = i / 3; int marker = game[i]; noFill(); strokeWeight(5); stroke(#000000); if (marker == CROSS) { line(xOffset + x * cellSize + cellSize / 6, yOffset + y * cellSize + cellSize / 6, xOffset + (x + 1) * cellSize - cellSize / 6, yOffset + (y + 1) * cellSize - cellSize / 6); line(xOffset + (x + 1) * cellSize - cellSize / 6, yOffset + y * cellSize + cellSize / 6, xOffset + x * cellSize + cellSize / 6, yOffset + (y + 1) * cellSize - cellSize / 6); } else if (marker == CIRCLE) { ellipse(xOffset + x * cellSize + cellSize / 2, yOffset + y * cellSize + cellSize / 2, 2*cellSize / 3, 2*cellSize / 3); } } } void checkWinner() { for (int[] indices : WINNING_COMBINATIONS) { int firstMarker = -1; boolean failed = false; for (int i = 0; i < indices.length; i++) { int marker = game[indices[i]]; if (firstMarker == -1) { firstMarker = marker; } else if (firstMarker != marker) { failed = true; } } if ((firstMarker == CROSS || firstMarker == CIRCLE) && !failed) { endGame(indices, firstMarker, false); return; } } int usedCells = 0; for (int i = 0; i < game.length; i++) { if (game[i] > NONE) { usedCells++; } } if (usedCells == game.length) { endGame(null, NONE, true); } } void endGame(int[] indices, int marker, boolean isDraw) { winnerIndices = indices; gameState = GAMEOVER; gameIsDraw = isDraw; StringList highScores = new StringList(); if (dataFile("highscores.txt").isFile()) { highScores.append(loadStrings("data/highscores.txt")); } String date = nf(year(), 4) + "-" + nf(month(), 2) + "-" + nf(day(), 2) + " " + nf(hour(), 2) + ":" + nf(minute(), 2) + ":" + nf(second(), 2); String text = isDraw ? "draw" : ((marker == CROSS ? "cross" : "circle") + "@" + join(nf(indices, 1), ",")); highScores.append(date + " / " + text); saveStrings("data/highscores.txt", highScores.array()); if (highScores.size() > 0) { int crossWins = 0; int circleWins = 0; int draw = 0; for (String s : highScores) { String winner = s.substring(22); if (winner.indexOf("@") > 0) { winner = winner.substring(0, winner.indexOf("@")); } if (winner.equals("cross")) crossWins++; else if (winner.equals("circle")) circleWins++; else if (winner.equals("draw")) draw++; } highScoreText = "\n\nHighScores:\nCross: " + crossWins + "\nCircle: " + circleWins + "\nDraw: " + draw; } } void startGame() { gameState = PLAYING; gameIsDraw = false; currentPlayer = NONE; game = new int[] { NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE }; winnerIndices = null; } void keyPressed() { switch (gameState) { case PLAYING: break; case GAMEOVER: case SPLASHSCREEN: startGame(); break; } } void mouseClicked() { switch (gameState) { case SPLASHSCREEN: case GAMEOVER: startGame(); break; case PLAYING: int x = (mouseX - xOffset) / cellSize; int y = (mouseY - yOffset) / cellSize; int idx = y * 3 + x; if (game[idx] != NONE) break; if (currentPlayer == NONE || currentPlayer == CIRCLE) { currentPlayer = CROSS; } else { currentPlayer = CIRCLE; } game[idx] = currentPlayer; break; } }
Code zu Snake
- Snake.pde
final int GAME_SPLASHSCREEN = 0; final int GAME_PLAYING = 1; final int GAME_OVER = 2; final int DIR_NONE = 0; final int DIR_UP = 1; final int DIR_DOWN = 2; final int DIR_LEFT = 4; final int DIR_RIGHT = 8; final int BOOSTER_FOOD = -1; final int BOOSTER_COFFEE = -2; final int BOOSTER_BROCCOLI = -4; final int SNAKE_HEAD = -8; final int SNAKE_BODY = -16; final color COL_SNAKE_BODY = #99ccff; final color COL_SNAKE_HEAD = #5588ff; final color COL_FOOD = #cf8a00; final color COL_BROCCOLI = #00b215; final color COL_COFFEE = #eac29a; final int BROCCOLI_DELAY_MIN = 500; final int BROCCOLI_DELAY_MAX = 2500; int gameState = GAME_SPLASHSCREEN; int cellSize = 16; int xOffset = 0; int yOffset = 0; int[] grid; int rows = 0; int columns = 0; int snakeHeadX = 0; int snakeHeadY = 0; int snakeDirection = DIR_NONE; int lastMove = 0; int currentHighScore = 0; int moveDelay = 100; int lastBroccoliMove = 0; int broccoliMoveDelay = 2500; int broccoliX = 0; int broccoliY = 0; String highScoreText = ""; String gameOverReason = ""; PFont headingFont; PFont textFont; PFont smallFont; void setup() { size(500, 500); headingFont = createFont("Liberation Sans Bold", 32); textFont = createFont("Liberation Sans", 16); smallFont = createFont("Liberation Sans", 12); xOffset = (width % cellSize) / 2; yOffset = (height % cellSize) / 2; ellipseMode(CENTER); } void draw() { switch (gameState) { case GAME_SPLASHSCREEN: background(#000000); fill(#FFFFFF); textAlign(CENTER, CENTER); textFont(headingFont); text("Snake", width / 2, height / 4); textFont(textFont); text("A single player game\n\nPress any key or click anywhere to start playing\n\n© 2022 Christian Weber", width / 2, height / 2); noStroke(); textAlign(CENTER, TOP); textFont(smallFont); fill(#FFFFFF); text("The snake\n(big head!)", width / 2, 3 * height / 4 - 20); drawCell(SNAKE_BODY, width / 2, 3 * height / 4 - 30); drawCell(SNAKE_BODY, width / 2 + cellSize, 3 * height / 4 - 30); drawCell(SNAKE_HEAD, width / 2 - cellSize, 3 * height / 4 - 30); fill(#FFFFFF); text("Food\n(+1 length)", width / 4, 3 * height / 4 + 55); drawCell(BOOSTER_FOOD, width / 4, 3 * height / 4 + 45); fill(#FFFFFF); text("Coffee\n(+3 length, +1 speed)", width / 2, 3 * height / 4 + 55); drawCell(BOOSTER_COFFEE, width / 2, 3 * height / 4 + 45); fill(#FFFFFF); text("Broccoli (Poison)\n(-3 length, -1 speed)\nBeware! Jumps around!", 3 * width / 4, 3 * height / 4 + 55); drawCell(BOOSTER_BROCCOLI, 3 * width / 4, 3 * height / 4 + 45); break; case GAME_PLAYING: background(#000000); drawGrid(); if (millis() - lastMove > moveDelay) { moveSnake(); lastMove = millis(); } if (millis() - lastBroccoliMove > broccoliMoveDelay) { randomBooster(BOOSTER_BROCCOLI); broccoliMoveDelay = (int)random(250, 5000); lastBroccoliMove = millis(); } break; case GAME_OVER: background(#000000); textAlign(CENTER, CENTER); drawGrid(); fill(#77000000); noStroke(); rectMode(CORNER); rect(xOffset, yOffset, columns * cellSize, rows * cellSize); fill(#FFFFFF); textFont(headingFont); text("Game Over", width / 2, height / 4); textFont(textFont); text(gameOverReason + "\n\nYou scored " + currentHighScore + " points.\n\nPress space or enter or click anywhere to play again." + highScoreText, width / 2, height / 2); break; } } void drawCell(int booster, int x, int y) { noStroke(); int radius = 4 * cellSize / 5; switch (booster) { case SNAKE_HEAD: fill(COL_SNAKE_HEAD); break; case SNAKE_BODY: fill(COL_SNAKE_BODY); radius = 3 * cellSize / 5; break; case BOOSTER_COFFEE: fill(COL_COFFEE); break; case BOOSTER_FOOD: fill(COL_FOOD); rectMode(CENTER); rect(x, y, radius, radius); return; case BOOSTER_BROCCOLI: fill(COL_BROCCOLI); radius = 2 * cellSize / 5; triangle(x - radius, y + radius, x + radius, y + radius, x, y - radius); return; } ellipse(x, y, radius, radius); } void drawGrid() { if (grid == null || grid.length <= 0) return; for (int x = 0; x < columns; x++) { for (int y = 0; y < rows; y++) { int cell = grid[x * columns + y]; int xPos = xOffset + x * cellSize + cellSize / 2; int yPos = yOffset + y * cellSize + cellSize / 2; if (cell > 0) { if (x == snakeHeadX && y == snakeHeadY) { drawCell(SNAKE_HEAD, xPos, yPos); } else { drawCell(SNAKE_BODY, xPos, yPos); } } else if (cell == BOOSTER_FOOD || cell == BOOSTER_COFFEE || cell == BOOSTER_BROCCOLI) { drawCell(cell, xPos, yPos); } } } stroke(#222222); for (int x = 0; x <= columns; x++) { line(xOffset + x * cellSize, yOffset, xOffset + x * cellSize, height - yOffset); } for (int y = 0; y <= rows; y++) { line(xOffset, yOffset + y * cellSize, width - xOffset, yOffset + y * cellSize); } } void startGame() { gameState = GAME_PLAYING; moveDelay = 100; columns = width / cellSize; rows = height / cellSize; grid = new int[columns * rows]; snakeHeadX = (int)random(columns / 3) + columns / 3; snakeHeadY = (int)random(rows / 3) + rows / 3; grid[snakeHeadX * columns + snakeHeadY] = 2; switch ((int)random(4) + 1) { case 1: grid[snakeHeadX * columns + (snakeHeadY + 1)] = 1; setDirection(DIR_UP); break; case 2: grid[snakeHeadX * columns + (snakeHeadY - 1)] = 1; setDirection(DIR_DOWN); break; case 3: grid[(snakeHeadX + 1) * columns + snakeHeadY] = 1; setDirection(DIR_LEFT); break; default: case 4: grid[(snakeHeadX - 1) * columns + snakeHeadY] = 1; setDirection(DIR_RIGHT); break; } for (int i = 0; i < 5; i++) { randomBooster(BOOSTER_FOOD); } randomBooster(BOOSTER_BROCCOLI); randomBooster(BOOSTER_COFFEE); } void randomBooster(int booster) { int x = (int)random(columns); int y = (int)random(rows); if (booster == BOOSTER_BROCCOLI) { grid[broccoliX * columns + broccoliY] = 0; } grid[x * columns + y] = booster; if (booster == BOOSTER_BROCCOLI) { broccoliX = x; broccoliY = y; } } void setDirection(int direction) { if (direction == DIR_UP || direction == DIR_DOWN || direction == DIR_LEFT || direction == DIR_RIGHT) { snakeDirection = direction; } } void moveSnake() { int head = grid[snakeHeadX * columns + snakeHeadY]; for (int x = 0; x < columns; x++) { for (int y = 0; y < rows; y++) { if (grid[x * columns + y] > 0) { grid[x * columns + y]--; } } } int newHeadIdx = -1; switch (snakeDirection) { case DIR_UP: if (snakeHeadY - 1 < 0) { endGame(head, "You hit the wall."); return; } newHeadIdx = snakeHeadX * columns + (snakeHeadY - 1); snakeHeadY--; break; case DIR_DOWN: if (snakeHeadY + 1 >= rows) { endGame(head, "You hit the wall."); return; } newHeadIdx = snakeHeadX * columns + (snakeHeadY + 1); snakeHeadY++; break; case DIR_LEFT: if (snakeHeadX - 1 < 0) { endGame(head, "You hit the wall."); return; } newHeadIdx = (snakeHeadX - 1) * columns + snakeHeadY; snakeHeadX--; break; case DIR_RIGHT: if (snakeHeadX + 1 >= columns) { endGame(head, "You hit the wall."); return; } newHeadIdx = (snakeHeadX + 1) * columns + snakeHeadY; snakeHeadX++; break; } if (grid[newHeadIdx] > 0) { endGame(head, "You tried to eat yourself. Creep."); return; } else if (grid[newHeadIdx] == BOOSTER_FOOD) { head++; randomBooster(BOOSTER_FOOD); } else if (grid[newHeadIdx] == BOOSTER_COFFEE) { moveDelay *= 0.9; head += 3; randomBooster(BOOSTER_COFFEE); } else if (grid[newHeadIdx] == BOOSTER_BROCCOLI) { moveDelay *= 1.1; head -= 3; randomBooster(BOOSTER_BROCCOLI); } if (head < 1) { endGame(0, "You ate too much broccoli."); return; } grid[newHeadIdx] = head; } void endGame(int score, String reason) { gameState = GAME_OVER; currentHighScore = score; gameOverReason = reason; StringList highScores = new StringList(); if (dataFile("highscores.txt").isFile()) { highScores.append(loadStrings("data/highscores.txt")); } String date = nf(year(), 4) + "-" + nf(month(), 2) + "-" + nf(day(), 2) + " " + nf(hour(), 2) + ":" + nf(minute(), 2) + ":" + nf(second(), 2); highScores.append(date + " / " + score); saveStrings("data/highscores.txt", highScores.array()); if (highScores.size() > 0) { int maxScore = 0; StringList dates = new StringList(); for (String s : highScores) { int parsedScore = parseInt(s.substring(22).trim()); if (maxScore < parsedScore) { maxScore = parsedScore; dates.clear(); } if (maxScore == parsedScore) { dates.append(s.substring(0, 20).trim()); } } dates.sort(); highScoreText = "\n\nHighScore: " + maxScore + " points, scored " + dates.size() + " times,\nfirst on " + dates.get(0) + ",\nlast on " + dates.get(dates.size() - 1) + "."; } } void keyPressed() { switch (gameState) { case GAME_SPLASHSCREEN: startGame(); break; case GAME_OVER: if (key == ' ' || keyCode == ENTER) { startGame(); } break; case GAME_PLAYING: if (key == 'n') { startGame(); } else if (keyCode == UP) { setDirection(DIR_UP); } else if (keyCode == DOWN) { setDirection(DIR_DOWN); } else if (keyCode == LEFT) { setDirection(DIR_LEFT); } else if (keyCode == RIGHT) { setDirection(DIR_RIGHT); } break; } } void mouseClicked() { switch (gameState) { case GAME_SPLASHSCREEN: case GAME_OVER: startGame(); break; case GAME_PLAYING: break; } }
Code zu Sokoban
Sketch
- Sokoban.pde
final int GAME_SPLASHSCREEN = 0; final int GAME_LEVEL_SELECT = 1; final int GAME_LEVEL_INFO = 2; final int GAME_PLAYING = 3; final int GAME_OVER = 4; int gameState = GAME_SPLASHSCREEN; PFont headingFont; PFont textFont; PFont smallFont; void setup() { size(500, 500); headingFont = createFont("Liberation Mono Bold", 32); textFont = createFont("Liberation Mono", 16); smallFont = createFont("Liberation Mono", 12); } void draw() { switch (gameState) { case GAME_SPLASHSCREEN: drawSplashScreen(); break; case GAME_LEVEL_SELECT: drawLevelSelect(); break; case GAME_LEVEL_INFO: drawLevelInfo(); break; case GAME_PLAYING: drawPlaying(); break; case GAME_OVER: drawGameOver(); break; } } void keyPressed() { switch (gameState) { case GAME_SPLASHSCREEN: keyPressedSplashScreen(); break; case GAME_LEVEL_SELECT: keyPressedLevelSelect(); break; case GAME_LEVEL_INFO: keyPressedLevelInfo(); break; case GAME_OVER: keyPressedGameOver(); break; case GAME_PLAYING: keyPressedPlaying(); break; } }
GAME_OVER
- GAME_OVER.pde
void drawGameOver() { background(#000000); textAlign(CENTER, CENTER); drawGrid(); fill(#AA000000); noStroke(); rectMode(CORNER); rect(0, 0, width, height); fill(#FFFFFF); if (selectedLevel < levels.length - 1) { textFont(headingFont); text("You solved this level!", width / 2, height / 4); textFont(textFont); text("You used " + moves.size() + " moves.\n\nPress enter to play next level.\nPress space to play this level again.\nPress l to select a different level.", width / 2, height / 2); } else { textFont(headingFont); text("You solved all levels!", width / 2, height / 4); textFont(textFont); text("You used " + moves.size() + " moves.\n\nPress space to play this level again.\nPress l to select a different level.", width / 2, height / 2); } textAlign(CENTER, BOTTOM); textFont(smallFont); text("Your Solution (" + moves.size() + " moves):\n" + formatMoves(join(moves.array(), ""), 60), width / 2, height - 10); } void keyPressedGameOver() { if (keyCode == ENTER) { if (selectedLevel < levels.length - 1) { selectedLevel++; startGame(selectedLevel); } } else if (key == ' ') { startGame(selectedLevel); } else if (key == 'l') { gameState = GAME_LEVEL_SELECT; } } String formatMoves(String moves, int maxLength) { StringList parts = new StringList(); while (moves.length() > maxLength) { parts.push(moves.substring(0, maxLength)); moves = moves.substring(maxLength); } parts.push(moves); parts.upper(); return join(parts.array(), "\n"); }
LEVEL_INFO
- LEVEL_INFO.pde
void drawLevelInfo() { background(#000000); drawGrid(); fill(#AA000000); noStroke(); rectMode(CORNER); rect(0, 0, width, height); fill(#FFFFFF); textAlign(CENTER, CENTER); textFont(headingFont); text(levels[selectedLevel][0], width / 2, height / 4); textFont(textFont); text("Created By:\n" + levels[selectedLevel][1], width / 2, height / 2); textAlign(CENTER, BOTTOM); textFont(textFont); text("Press any key to hide this screen.\nPress arrow keys to start playing.", width / 2, height - 10); } void keyPressedLevelInfo() { if (key == 'l') { gameState = GAME_LEVEL_SELECT; } else { gameState = GAME_PLAYING; keyPressedPlaying(); } }
LEVEL_SELECT
- LEVEL_SELECT.pde
int selectedLevel = 0; int unlockedLevel = 0; int cellSize = 16; int xOffset = 0; int yOffset = 0; boolean hasMultipleTargetsOrBoxes = false; String[] grid; String[] objects; int rows = 0; int columns = 0; StringList moves = new StringList(); void drawLevelSelect() { background(#000000); textAlign(CENTER, CENTER); textFont(headingFont); fill(#FFFFFF); text("Select a level", width / 2, 32); textFont(textFont); text("UP / DOWN to select, ENTER to play", width / 2, 75); for (int i = 0; i < levels.length; i++) { if (selectedLevel == i && i > unlockedLevel) { fill(#FF0000); } else if (selectedLevel == i) { fill(#00FF00); } else if (i > unlockedLevel) { fill(#555555); } else { fill(#FFFFFF); } text(levels[i][0], width / 2, 120 + i * 20); } } void keyPressedLevelSelect() { if (keyCode == ENTER) { startGame(selectedLevel); } else if (keyCode == UP) { selectedLevel--; } else if (keyCode == DOWN) { selectedLevel++; } if (selectedLevel < 0) { selectedLevel = levels.length - 1; } else if (selectedLevel >= levels.length) { selectedLevel = 0; } } void startGame(int level) { if (level > unlockedLevel) return; gameState = GAME_LEVEL_INFO; moves.clear(); String[] levelData = levels[level]; grid = new String[levelData.length - 2]; objects = new String[levelData.length - 2]; StringList targets = new StringList(); StringList boxes = new StringList(); for (int y = 0; y < levelData.length - 2; y++) { grid[y] = levelData[y + 2].replaceAll("[\\$a-z]", " ").replaceAll("\\.", "Z").replaceAll("\\*", "Z"); objects[y] = levelData[y + 2].replaceAll("@", "z").replaceAll("\\*", "z").replaceAll("[^a-z]", " "); for (int x = 0; x < grid[y].length(); x++) { char tile = grid[y].charAt(x); char box = objects[y].charAt(x); if (!targets.hasValue("" + tile) && tile >= 'A' && tile <= 'Z') { targets.push("" + tile); } if (!boxes.hasValue("" + box) && box >= 'a' && box <= 'z') { boxes.push("" + box); } } if (levelData[y + 2].indexOf("$") >= 0) { playerY = y; playerX = levelData[y + 2].indexOf("$"); } } targets.sort(); targets.lower(); boxes.sort(); hasMultipleTargetsOrBoxes = targets.size() > 1 || boxes.size() > 1 || !join(targets.array(), "").equals(join(boxes.array(), "")); columns = grid[0].length(); rows = grid.length; if (columns > rows) { cellSize = (width - 20) / columns; } else { cellSize = (height - 20) / rows; } xOffset = (width - cellSize * columns) / 2; yOffset = (height - cellSize * rows) / 2; }
PLAYING
- PLAYING.pde
final int DIR_NONE = 0; final int DIR_UP = 1; final int DIR_DOWN = 2; final int DIR_LEFT = 4; final int DIR_RIGHT = 8; int playerX = 0; int playerY = 0; void drawPlaying() { background(#000000); drawGrid(); } void drawPlayer(int x, int y) { fill(#0000FF); noStroke(); ellipseMode(CENTER); ellipse(xOffset + x * cellSize + cellSize / 2, yOffset + y * cellSize + cellSize / 2, 4 * cellSize / 5, 4 * cellSize / 5); } void drawObject(char object, int x, int y, boolean fittingHere) { if (object < 'a' || object > 'z') return; noStroke(); fill(fittingHere ? #00FF00 : #FFFF00); rectMode(CENTER); rect(xOffset + x * cellSize + cellSize / 2, yOffset + y * cellSize + cellSize / 2, 4 * cellSize / 5, 4 * cellSize / 5); if (hasMultipleTargetsOrBoxes) { textFont(headingFont); textAlign(CENTER, CENTER); fill(#000000); text(object, xOffset + x * cellSize + cellSize / 2, yOffset + y * cellSize + cellSize / 2); } } void drawTile(char tile, int x, int y) { if (tile != '#' && (tile < 'A' || tile > 'Z')) return; if (tile == '#') { fill(#FFFFFF); } else if (tile >= 'A' && tile <= 'Z') { fill(#FF0000); } noStroke(); rectMode(CENTER); rect(xOffset + x * cellSize + cellSize / 2, yOffset + y * cellSize + cellSize / 2, cellSize, cellSize); if (hasMultipleTargetsOrBoxes && tile >= 'A' && tile <= 'Z') { textFont(headingFont); textAlign(CENTER, CENTER); fill(#000000); text(tile, xOffset + x * cellSize + cellSize / 2, yOffset + y * cellSize + cellSize / 2); } } void drawGrid() { if (grid == null || grid.length <= 0) return; for (int y = 0; y < rows; y++) { String tileRow = grid[y]; String objectRow = objects[y]; for (int x = 0; x < columns; x++) { char tile = tileRow.charAt(x); char object = objectRow.charAt(x); drawTile(tile, x, y); drawObject(object, x, y, ("" + tile).equalsIgnoreCase("" + object)); if (playerX == x && playerY == y) { drawPlayer(x, y); } } } stroke(#222222); for (int x = 0; x <= columns; x++) { line(xOffset + x * cellSize, yOffset, xOffset + x * cellSize, height - yOffset); } for (int y = 0; y <= rows; y++) { line(xOffset, yOffset + y * cellSize, width - xOffset, yOffset + y * cellSize); } } void move(int direction) { char tile; char object; char nextTile; char nextObject; switch (direction) { case DIR_UP: moves.append("U"); if (playerY <= 0) return; tile = grid[playerY - 1].charAt(playerX); object = objects[playerY - 1].charAt(playerX); if (object >= 'a' && object <= 'z') { if (playerY <= 1) return; nextTile = grid[playerY - 2].charAt(playerX); nextObject = objects[playerY - 2].charAt(playerX); if (nextTile != '#' && nextObject == ' ') { objects[playerY - 1] = objects[playerY - 1].substring(0, playerX) + ' ' + objects[playerY - 1].substring(playerX + 1); objects[playerY - 2] = objects[playerY - 2].substring(0, playerX) + object + objects[playerY - 2].substring(playerX + 1); playerY--; } } else if (tile != '#') { playerY--; } break; case DIR_DOWN: moves.append("D"); if (playerY >= rows) return; tile = grid[playerY + 1].charAt(playerX); object = objects[playerY + 1].charAt(playerX); if (object >= 'a' && object <= 'z') { if (playerY >= rows - 1) return; nextTile = grid[playerY + 2].charAt(playerX); nextObject = objects[playerY + 2].charAt(playerX); if (nextTile != '#' && nextObject == ' ') { objects[playerY + 1] = objects[playerY + 1].substring(0, playerX) + ' ' + objects[playerY + 1].substring(playerX + 1); objects[playerY + 2] = objects[playerY + 2].substring(0, playerX) + object + objects[playerY + 2].substring(playerX + 1); playerY++; } } else if (tile != '#') { playerY++; } break; case DIR_LEFT: moves.append("L"); if (playerX <= 0) return; tile = grid[playerY].charAt(playerX - 1); object = objects[playerY].charAt(playerX - 1); if (object >= 'a' && object <= 'z') { if (playerX <= 1) return; nextTile = grid[playerY].charAt(playerX - 2); nextObject = objects[playerY].charAt(playerX - 2); if (nextTile != '#' && nextObject == ' ') { objects[playerY] = objects[playerY].substring(0, playerX - 1) + ' ' + objects[playerY].substring(playerX - 1 + 1); objects[playerY] = objects[playerY].substring(0, playerX - 2) + object + objects[playerY].substring(playerX - 2 + 1); playerX--; } } else if (tile != '#') { playerX--; } break; case DIR_RIGHT: moves.append("R"); if (playerX >= columns) return; tile = grid[playerY].charAt(playerX + 1); object = objects[playerY].charAt(playerX + 1); if (object >= 'a' && object <= 'z') { if (playerX >= columns - 1) return; nextTile = grid[playerY].charAt(playerX + 2); nextObject = objects[playerY].charAt(playerX + 2); if (nextTile != '#' && nextObject == ' ') { objects[playerY] = objects[playerY].substring(0, playerX + 1) + ' ' + objects[playerY].substring(playerX + 1 + 1); objects[playerY] = objects[playerY].substring(0, playerX + 2) + object + objects[playerY].substring(playerX + 2 + 1); playerX++; } } else if (tile != '#') { playerX++; } break; } boolean hasWrongObjects = false; for (int y = 0; y < rows; y++) { for (int x = 0; x < columns; x++) { object = objects[y].charAt(x); tile = grid[y].charAt(x); if (object >= 'a' && object <= 'z') { if (!("" + object).equalsIgnoreCase("" + tile)) { hasWrongObjects = true; } } } } if (!hasWrongObjects) { endGame(); } } void keyPressedPlaying() { if (key == 'r') { startGame(selectedLevel); } else if (key == 'l') { gameState = GAME_LEVEL_SELECT; } else if (keyCode == UP) { move(DIR_UP); } else if (keyCode == DOWN) { move(DIR_DOWN); } else if (keyCode == LEFT) { move(DIR_LEFT); } else if (keyCode == RIGHT) { move(DIR_RIGHT); } } void endGame() { if (selectedLevel >= unlockedLevel) { unlockedLevel = selectedLevel + 1; } gameState = GAME_OVER; }
SPLASHSCREEN
- SPLASHSCREEN.pde
String cheat = ""; void drawSplashScreen() { background(#000000); fill(#FFFFFF); textAlign(CENTER, CENTER); textFont(headingFont); text("Sokoban", width / 2, height / 4); textFont(textFont); text("A single player game.\n\nPress any key start the game with the first level.\n\n© 2022 Christian Weber", width / 2, height / 2); } void keyPressedSplashScreen() { if (key == 'w' || key == 'i' || key == 'n' || key == 't' || key == 'e' || key == 'r') { cheat += key; if (cheat.equals("winter")) { unlockedLevel = 9999; gameState = GAME_LEVEL_SELECT; } } else { startGame(0); } }
ZLevels
- ZLevels.pde
String[][] levels = { { "Tutorial 01", "Christian Weber - chweber.de\n\nMove box to target", "#######", "#$a A#", "#######" }, { "Tutorial 02", "Christian Weber - chweber.de\n\nMove box to any target", "#######", "# $ #", "#A a A#", "#######" }, { "Tutorial 03", "Christian Weber - chweber.de\n\nMove box to specific target", "########", "# B#", "#$b A#", "# B#", "########" }, { "Tutorial 04", "Christian Weber - chweber.de\n\nMove boxes to specific targets", "#######", "# #", "# b A#", "#$ #", "# a B#", "# #", "#######" }, { "Soko 01", "Brian Kent - kentpw@norwich.net\nhttp://www.aenigmafonts.com/games/sokoban2.html", " ### ", " #.# ", " #####.##### ", " ## ## ", "## # # # # ##", "# ## ## #", "# ## # # ## #", "# @$@ #", "#### ### ####", " #### #### " }, { "Soko 02", "Brian Kent - kentpw@norwich.net\nhttp://www.aenigmafonts.com/games/sokoban2.html", " ########### ", "## ## ", "# @ @ # ", "# @# #.# #@ # ", "# #*# #####", "# ###.### # #", "# .*.$.*. #", "# ###.### # #", "# #*# #####", "# @# #.# #@ # ", "# @ @ # ", "## ## ", " ########### " }, { "Soko 03", "Brian Kent - kentpw@norwich.net\nhttp://www.aenigmafonts.com/games/sokoban2.html", " ##### ", " ## # ", " # # ", " ### ######", " #.#.# ##. #", "### ### ## #", "# # @ ## ##", "# @$@ #", "# # @ # #", "###### ### ##", " # .## #### # ", " # # ", " ## ######### ", " #### ", }, { "Soko 04", "Brian Kent - kentpw@norwich.net\nhttp://www.aenigmafonts.com/games/sokoban2.html", " ##### ", " ## ## ", " ### @ @ ### ", " ## # # ## ", "## ##", "# @# ... #@ #", "# .$. #", "# @# ... #@ #", "## ##", " ## # # ## ", " ### @ @ ### ", " ## ## ", " ##### " }, { "Soko 05", "Brian Kent - kentpw@norwich.net\nhttp://www.aenigmafonts.com/games/sokoban2.html", " ##### ", " # # ", " # # ########## ", "#### # # # # ", "# # @ @ ##### ", "# ## #@ # # # # ", "# @ # ## ## @ @ # ", "# ## ##### # # # ", "# @ # # ###### ## ", "### @ $ ...# # # ", " # # ...# # # ", " ##### ###...# # ###", " #### ##### # #", " # @ #", " # #", " #####" }, { "Soko 06", "Brian Kent - kentpw@norwich.net\nhttp://www.aenigmafonts.com/games/sokoban2.html", " # # ", " ######### ", " # # ", " ### ### ", " # # # # # # ", "###### #.# ######", " # @. .@ # ", " # # #@# # # ", " # . @$@ . # ", " # # #@# # # ", " # @. .@ # ", "###### #.# ######", " # # # # # # ", " ### ### ", " # # ", " ######### ", " # # " }, { "Soko 07", "Brian Kent - kentpw@norwich.net\nhttp://www.aenigmafonts.com/games/sokoban2.html", "#### ####", "# .#######. #", "#. .#", "## ## ## ##", " # # # # ", " # #@# # ", " # @$@ # ", " # #@# # ", " ### ### ", "##.## # ##.##", "# @ # @ #", "# ## # ## #", "## #. .# ##", " # @ @ # ", " # ## # ## # ", " # # ", " ########### " }, { "Soko 08", "Brian Kent - kentpw@norwich.net\nhttp://www.aenigmafonts.com/games/sokoban2.html", "#### ", "# # #####", "# ########## #", "###.# # ### #", " #.# @#@# # ###", " #.# # @ # ", " #.##@# # # # ", " # # # ", " #$ ### # ", " # ### ", " #.##@#@# # ", " #.# # # ## ", " #.# @# @ # ", "###.# # # ", "# ######## ### ", "# # #### # ", "#### # # ", " #### " } };
Schwierigkeitsstufe 4: Sokoban mit Grafik
Code zu Sokoban mit Grafik