diff --git a/.DS_Store b/.DS_Store
deleted file mode 100644
index a9921da..0000000
Binary files a/.DS_Store and /dev/null differ
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..21b6bb7
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,22 @@
+# ===== Build =====
+cmake-build-*/
+build/
+out/
+
+# ===== IDE =====
+.idea/
+*.iml
+
+# ===== OS =====
+.DS_Store
+
+# ===== Compiled =====
+*.o
+*.obj
+*.a
+*.so
+*.dylib
+*.exe
+
+# ===== Logs =====
+*.log
diff --git a/.idea/.gitignore b/.idea/.gitignore
deleted file mode 100644
index 13566b8..0000000
--- a/.idea/.gitignore
+++ /dev/null
@@ -1,8 +0,0 @@
-# Default ignored files
-/shelf/
-/workspace.xml
-# Editor-based HTTP Client requests
-/httpRequests/
-# Datasource local storage ignored files
-/dataSources/
-/dataSources.local.xml
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
deleted file mode 100644
index d843f34..0000000
--- a/.idea/vcs.xml
+++ /dev/null
@@ -1,4 +0,0 @@
-
-
-
-
\ No newline at end of file
diff --git a/.vscode/launch.json b/.vscode/launch.json
deleted file mode 100644
index 9dae3d4..0000000
--- a/.vscode/launch.json
+++ /dev/null
@@ -1,10 +0,0 @@
-{
- // Use IntelliSense to learn about possible attributes.
- // Hover to view descriptions of existing attributes.
- // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
- "version": "0.2.0",
- "configurations": [
-
-
- ]
-}
\ No newline at end of file
diff --git a/src/gamecube.cpp b/src/gamecube.cpp
index 274e6d5..0e03c65 100644
--- a/src/gamecube.cpp
+++ b/src/gamecube.cpp
@@ -1,6 +1,7 @@
+#include "raylib.h"
#include "gamecube.h"
-gamecube::gamecube(const Vec3 &pos, Color col)
+gamecube::gamecube(const Vec3& pos, Color col)
: position(pos), color(col) {}
void gamecube::Update(float flipSpeed)
@@ -27,8 +28,8 @@ void gamecube::Update(float flipSpeed)
}
}
-void gamecube::FlipForward() { flippingForward = true; }
-void gamecube::FlipBackward() { flippingBackward = true; }
+void gamecube::FlipForward() { flippingForward = true; }
+void gamecube::FlipBackward() { flippingBackward = true; }
bool gamecube::IsFlipped() const { return flipped; }
bool gamecube::IsMatched() const { return matched; }
@@ -38,29 +39,28 @@ void gamecube::Draw() const
{
rlPushMatrix();
- // Matrizen für Rotation und Translation erzeugen
- auto matrix_a = Matrix3D::gameMatrix::translate({ position.x, position.y, position.z});
+ auto matrix_a = Matrix3D::gameMatrix::translate(
+ { position.x, position.y, position.z }
+ );
auto matrix_b = Matrix3D::gameMatrix::rot3D(rotation, 'y');
-
- // Matrizen multiplizieren (Translation * Rotation)
auto model = Matrix3D::gameMatrix::matmul(matrix_a, matrix_b);
- // transform for raylib matrix
float f[16];
for (int i = 0; i < 4; i++)
for (int j = 0; j < 4; j++)
f[j * 4 + i] = model[i][j];
+
rlMultMatrixf(f);
if (rotation < 90.0f)
- DrawCube({0,0,0}, 1,1,1, GRAY);
+ DrawCube({ 0, 0, 0 }, 1, 1, 1, GRAY);
else
- DrawCube({0,0,0}, 1,1,1, color);
+ DrawCube({ 0, 0, 0 }, 1, 1, 1, color);
- DrawCubeWires({0,0,0}, 1,1,1, BLACK);
+ DrawCubeWires({ 0, 0, 0 }, 1, 1, 1, BLACK);
rlPopMatrix();
}
-Vec3 gamecube::GetPosition() const { return position; }
-float gamecube::GetRotationY() const { return rotation; }
\ No newline at end of file
+Vec3 gamecube::GetPosition() const { return position; }
+float gamecube::GetRotationY() const { return rotation; }
diff --git a/src/gamematrix.cpp b/src/gamematrix.cpp
index 3a20980..e8eb7a8 100644
--- a/src/gamematrix.cpp
+++ b/src/gamematrix.cpp
@@ -37,9 +37,9 @@ namespace Matrix3D
{
Mat4 result = identity();
- result[0][3] = pos[0]; // x
- result[1][3] = pos[1]; // y
- result[2][3] = pos[2]; // z
+ result[0][3] = pos[0];
+ result[1][3] = pos[1];
+ result[2][3] = pos[2];
return result;
}
@@ -56,16 +56,16 @@ namespace Matrix3D
switch (axis)
{
case 'x':
- result[1][1] = c; result[1][2] = -s;
- result[2][1] = s; result[2][2] = c;
+ result[1][1] = c; result[1][2] = -s;
+ result[2][1] = s; result[2][2] = c;
break;
case 'y':
- result[0][0] = c; result[0][2] = s;
+ result[0][0] = c; result[0][2] = s;
result[2][0] = -s; result[2][2] = c;
break;
case 'z':
- result[0][0] = c; result[0][1] = -s;
- result[1][0] = s; result[1][1] = c;
+ result[0][0] = c; result[0][1] = -s;
+ result[1][0] = s; result[1][1] = c;
break;
default:
break;
@@ -80,7 +80,7 @@ namespace Matrix3D
Vec3 operator*(const Mat4& m, const Vec3& v)
{
- Vec4 v_hom = {v[0], v[1], v[2], 1.0};
+ Vec4 v_hom = { v[0], v[1], v[2], 1.0 };
Vec4 res_hom = {};
for (int i = 0; i < 4; ++i)
@@ -91,7 +91,6 @@ namespace Matrix3D
}
}
- return {res_hom[0], res_hom[1], res_hom[2]};
+ return { res_hom[0], res_hom[1], res_hom[2] };
}
-
-}
\ No newline at end of file
+}
diff --git a/src/main.cpp b/src/main.cpp
index dc271d2..b8a62a4 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -1,202 +1,228 @@
#include "gamecube.h"
#include
#include
+#include
+#include "raylib.h"
+enum GameScreen { MENU = 0, GAMEPLAY };
-enum class GameState
+static void SetupGame(std::vector& cubes, int pairs)
{
- Idle, // kein Würfel offen, Eingabe erlaubt
- OneFlipped, // ein Würfel offen
- CheckingMatch, // zwei Würfel vollständig aufgeklappt, Vergleich läuft
- LockInput // Würfel drehen gerade – Eingabe kurz blockiert
-};
+ cubes.clear();
+ Color colors[] = { RED, GREEN, BLUE, YELLOW, ORANGE, PURPLE, WHITE, SKYBLUE };
+ std::vector positions;
+
+ const int count = pairs * 2;
+ int cols = 3;
+ int rows = 2;
+
+ if (count > 6) {
+ cols = 4;
+ rows = 3;
+ }
+
+ int index = 0;
+ for (int r = 0; r < rows && index < count; ++r) {
+ for (int c = 0; c < cols && index < count; ++c) {
+ positions.push_back({ (float)c * 2.0f - (cols - 1), 0.0f, (float)r * 2.0f - (rows - 1) });
+ index++;
+ }
+ }
+
+ std::vector colorPool;
+ colorPool.reserve(count);
+
+ for (int i = 0; i < pairs; i++) {
+ colorPool.push_back(colors[i]);
+ colorPool.push_back(colors[i]);
+ }
+
+ for (int i = (int)colorPool.size() - 1; i > 0; --i) {
+ int j = rand() % (i + 1);
+ std::swap(colorPool[i], colorPool[j]);
+ }
+
+ for (int i = 0; i < count; i++) {
+ cubes.emplace_back(positions[i], colorPool[i]);
+ }
+}
-// -----------------------------------------------------------
-// 3D Memory Game – Hauptprogramm
-// -----------------------------------------------------------
int main()
{
- // Zufall initialisieren
- srand(time(NULL));
+ srand((unsigned)time(NULL));
+
double startTime = 0.0;
double endTime = 0.0;
bool timerStarted = false;
+ GameScreen currentScreen = MENU;
+ int selectedPairs = 3;
+
+ std::vector cubes;
+ gamecube* first = nullptr;
+ gamecube* second = nullptr;
+ float flipSpeed = 5.0f;
+ bool gameWon = false;
- // Fenster und Kamera
InitWindow(800, 600, "3D Memory Game with Matrix3D Library");
SetTargetFPS(60);
Camera3D camera{};
- camera.position = {6.0f, 6.0f, 6.0f};
- camera.target = {0.0f, 0.0f, 0.0f};
- camera.up = {0.0f, 1.0f, 0.0f};
- camera.fovy = 45.0f;
+ camera.position = { 6.0f, 6.0f, 6.0f };
+ camera.target = { 0.0f, 0.0f, 0.0f };
+ camera.up = { 0.0f, 1.0f, 0.0f };
+ camera.fovy = 45.0f;
camera.projection = CAMERA_PERSPECTIVE;
- // Nur 3 Farben für 3 Paare
- Color colors[] = { RED, GREEN, BLUE };
-
- // 6 Karten-Positionen im 3x2 Raster
- std::vector positions = {{-2, 0, -2}, {0, 0, -2}, {2, 0, -2},{-2, 0, 0}, {0, 0, 0}, {2, 0, 0}};
-
- // Farben doppelt in einen Pool legen und mischen
- std::vector colorPool;
- for (int i = 0; i < 3; i++)
- {
- colorPool.push_back(colors[i]);
- colorPool.push_back(colors[i]);
- }
-
- // Fisher-Yates Shuffle mit rand()
- for (int i = colorPool.size() - 1; i > 0; --i)
- {
- int j = rand() % (i + 1); // Zufallsindex von 0 bis i
- std::swap(colorPool[i], colorPool[j]);
- }
-
- // Karten/Würfel erstellen
- std::vector cubes;
- for (int i = 0; i < 6; i++)
- cubes.emplace_back(positions[i], colorPool[i]);
-
- gamecube* first = nullptr;
- gamecube* second = nullptr;
- float flipSpeed = 5.0f; // Drehgeschwindigkeit
- bool gameWon = false;
-
- GameState state = GameState::Idle; // Start Zustand
-
- // -----------------------------------------------------------
- // Hauptspielschleife
- // -----------------------------------------------------------
while (!WindowShouldClose())
{
- if (!timerStarted)
+ switch (currentScreen)
{
- startTime = GetTime();
- timerStarted = true;
- }
- // Klick-Erkennung
- if (!gameWon
- && state != GameState::LockInput
- && state != GameState::CheckingMatch
- && IsMouseButtonPressed(MOUSE_LEFT_BUTTON))
- {
- Vector2 mouse = GetMousePosition();
-
- for (auto &c : cubes)
+ case MENU:
{
- if (!c.IsFlipped() && !c.IsMatched())
+ if (IsKeyPressed(KEY_THREE)) {
+ selectedPairs = 3;
+ SetupGame(cubes, selectedPairs);
+ gameWon = false;
+ first = second = nullptr;
+ currentScreen = GAMEPLAY;
+ timerStarted = false;
+ }
+ else if (IsKeyPressed(KEY_SIX)) {
+ selectedPairs = 6;
+ SetupGame(cubes, selectedPairs);
+ gameWon = false;
+ first = second = nullptr;
+ currentScreen = GAMEPLAY;
+ timerStarted = false;
+ }
+ break;
+ }
+
+ case GAMEPLAY:
+ {
+ if (!timerStarted) {
+ startTime = GetTime();
+ timerStarted = true;
+ }
+
+ if (!gameWon && IsMouseButtonPressed(MOUSE_LEFT_BUTTON) && (second == nullptr))
{
- Vector2 screenPos = GetWorldToScreen({c.GetPosition().x, c.GetPosition().y, c.GetPosition().z}, camera);
+ Vector2 mouse = GetMousePosition();
+ Ray ray = GetMouseRay(mouse, camera);
- if (fabs(mouse.x - screenPos.x) < 40 && fabs(mouse.y - screenPos.y) < 40)
+ gamecube* hitCube = nullptr;
+ float bestDist = 1e9f;
+
+ for (auto& c : cubes)
{
- c.FlipForward();
+ if (c.IsMatched() || c.IsFlipped()) continue;
- // ZUSTANDSUMSCHALTLOGIK
- if (state == GameState::Idle)
- {
- // 1. Click
- c.FlipForward();
- first = &c;
- state = GameState::OneFlipped;
- break; // wichtig!!! Wir verlassen die Schleife :)
+ auto p = c.GetPosition();
+
+ BoundingBox box;
+ box.min = { p.x - 0.75f, p.y - 0.75f, p.z - 0.75f };
+ box.max = { p.x + 0.75f, p.y + 0.75f, p.z + 0.75f };
+
+ RayCollision col = GetRayCollisionBox(ray, box);
+ if (col.hit && col.distance < bestDist) {
+ bestDist = col.distance;
+ hitCube = &c;
}
- else if (state == GameState::OneFlipped)
- {
- // 2. Click
- // Lass uns überprüfen, ob es NICHT derselbe Würfel ist.
- if (&c != first)
- {
- c.FlipForward();
- second = &c;
- state = GameState::LockInput;
- break; // wichtig!!! Wir verlassen die Schleife :)
- }
+ }
+
+ if (hitCube)
+ {
+ if (!first) {
+ first = hitCube;
+ first->FlipForward();
+ }
+ else if (hitCube != first) {
+ second = hitCube;
+ second->FlipForward();
}
}
}
+
+ for (auto& c : cubes) {
+ c.Update(flipSpeed);
+ }
+
+ if (first && second && first->IsFlipped() && second->IsFlipped())
+ {
+ Color col1 = first->GetColor();
+ Color col2 = second->GetColor();
+
+ if (col1.r == col2.r && col1.g == col2.g && col1.b == col2.b) {
+ first->SetMatched(true);
+ second->SetMatched(true);
+ }
+ else {
+ first->FlipBackward();
+ second->FlipBackward();
+ }
+
+ first = nullptr;
+ second = nullptr;
+ }
+
+ if (!gameWon)
+ {
+ gameWon = std::all_of(
+ cubes.begin(),
+ cubes.end(),
+ [](const gamecube& c) { return c.IsMatched(); }
+ );
+
+ if (gameWon) {
+ endTime = GetTime() - startTime;
+ }
+ }
+ break;
}
}
- // Animation aller Würfel
- bool animationBusy = false;
-
- for (auto &c : cubes)
- {
- c.Update(flipSpeed);
- }
-
- if (state == GameState::LockInput)
- {
- if (first && first->IsFlipped() && second && second->IsFlipped())
- {
- state = GameState::CheckingMatch;
- }
- }
-
- // Matching-Logik
- if (state == GameState::CheckingMatch && first && second)
- {
- if (first->GetColor().r == second->GetColor().r &&
- first->GetColor().g == second->GetColor().g &&
- first->GetColor().b == second->GetColor().b)
- {
- first->SetMatched(true);
- second->SetMatched(true);
- }
- else
- {
- first->FlipBackward();
- second->FlipBackward();
- }
-
- first = second = nullptr;
- state = GameState::Idle;
- }
-
- // Gewinnprüfung
- if (!gameWon)
- {
- gameWon = std::all_of(cubes.begin(), cubes.end(), [](const gamecube &c){ return c.IsMatched(); });
- if (gameWon)
- {
- endTime = GetTime() - startTime;
- }
- }
- // -----------------------------------------------------------
- // Zeichnen
- // -----------------------------------------------------------
BeginDrawing();
ClearBackground(RAYWHITE);
- BeginMode3D(camera);
- for (auto &c : cubes)
- c.Draw();
-
- EndMode3D();
-
- if (gameWon)
+ switch (currentScreen)
{
- DrawText("Congrats! You found all pairs!", 150, 260, 30, DARKBLUE);
+ case MENU:
+ {
+ DrawText("3D MEMORY SPIEL",
+ GetScreenWidth() / 2 - MeasureText("3D MEMORY SPIEL", 50) / 2,
+ 80, 50, DARKGRAY);
+ DrawText("Waehlen Sie die Spielgroesse:", 50, 200, 25, BLACK);
+ DrawText("Druecken Sie [3] fuer 3 Paare (6 Wuerfel)", 50, 240, 20, BLUE);
+ DrawText("Druecken Sie [6] fuer 6 Paare (12 Wuerfel)", 50, 280, 20, BLUE);
+ break;
+ }
- char buffer[64];
- sprintf(buffer, "Cleared in %.2f seconds", endTime);
- DrawText(buffer, 150, 300, 28, DARKGREEN);
- }
- else
- {
- DrawText("Flip 2 cubes - find matching pairs!", 10, 10, 20, DARKGRAY);
- char liveBuf[64];
- sprintf(liveBuf, "Time: %.2f", GetTime() - startTime);
- DrawText(liveBuf, 10, 40, 20, DARKGRAY);
+ case GAMEPLAY:
+ {
+ BeginMode3D(camera);
+ for (auto& c : cubes) c.Draw();
+ EndMode3D();
+
+ if (gameWon) {
+ DrawText("Congrats! You found all pairs!", 150, 260, 30, DARKBLUE);
+ char buffer[64];
+ sprintf(buffer, "Cleared in %.2f seconds", endTime);
+ DrawText(buffer, 150, 300, 28, DARKGREEN);
+ } else {
+ DrawText("Flip 2 cubes - find matching pairs!", 10, 10, 20, DARKGRAY);
+ char liveBuf[64];
+ sprintf(liveBuf, "Time: %.2f", GetTime() - startTime);
+ DrawText(liveBuf, 10, 40, 20, DARKGRAY);
+ }
+ break;
+ }
}
+
EndDrawing();
}
CloseWindow();
return 0;
-}
\ No newline at end of file
+}