Compare commits
1 Commits
Author | SHA1 | Date | |
---|---|---|---|
f18c8c3fe3 |
@@ -5,8 +5,6 @@ This is a simple random walker that has a little twist that if the user clicks t
|
|||||||
|
|
||||||
The user can user the number 1, 2, 3, 4 and 5 keys to change the force at which the walker is blown away from the mouse pointer. 1 is lowest force and 5 is highest.
|
The user can user the number 1, 2, 3, 4 and 5 keys to change the force at which the walker is blown away from the mouse pointer. 1 is lowest force and 5 is highest.
|
||||||
|
|
||||||
There are now 4 red impervious obstacles to bump into.
|
|
||||||
|
|
||||||
test.cpp builds to TestWindowApp as a test to help me understand how ReWindow works.
|
test.cpp builds to TestWindowApp as a test to help me understand how ReWindow works.
|
||||||
|
|
||||||
main.cpp builds to ReWalkerApp which is the main random walker program.
|
main.cpp builds to ReWalkerApp which is the main random walker program.
|
||||||
@@ -19,7 +17,7 @@ Install dependencies
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
Rocky/Fedora/RHEL: dnf install cmake make gcc-g++ libX11 libX11-devel mesa-libGL-devel
|
Rocky/Fedora/RHEL: dnf install cmake make gcc-g++ libX11 libX11-devel mesa-libGL-devel
|
||||||
Pop_OS!/Ubuntu/Debian: apt-get install cmake make gcc g++ libx11-6 libx11-dev libgl-dev libxrandr-dev libvulkan-dev
|
Pop_OS!/Ubuntu/Debian: apt-get install cmake make gcc g++ libx11-6 libx11-dev libgl-dev libxrandr-dev
|
||||||
```
|
```
|
||||||
|
|
||||||
Clone the repository
|
Clone the repository
|
||||||
|
174
main.cpp
174
main.cpp
@@ -1,9 +1,9 @@
|
|||||||
// ReWalker
|
// ReWalker
|
||||||
// A random walker that moves around the screen with a fading trail, mouse interaction, and a status bar.
|
// A random walker that moves around the screen with a fading trail and mouse interaction.
|
||||||
// There are now obsticalse you cant walk through
|
// Now with real-time adjustable push force using number keys 1-5!
|
||||||
// Updated to use the latest ReWindow API and JGL rendering.
|
// Written using Redacted ReWindow https://git.redacted.cc/Redacted/ReWindow
|
||||||
// Written by Rich
|
// Written by Rich
|
||||||
// With help from (and thanks to) from Maxine, Josh, and Bill.
|
// With help from (and thanks to) from Maxine, Josh and Bill
|
||||||
|
|
||||||
#include <ReWindow/types/Window.h>
|
#include <ReWindow/types/Window.h>
|
||||||
#include <ReWindow/Logger.h>
|
#include <ReWindow/Logger.h>
|
||||||
@@ -11,97 +11,70 @@
|
|||||||
#include <Color4.hpp>
|
#include <Color4.hpp>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
|
// Define window size and dot size (dot size should be odd for symmetry)
|
||||||
// Define window size and dot size
|
|
||||||
#define WIDTH 800
|
#define WIDTH 800
|
||||||
#define HEIGHT 600
|
#define HEIGHT 600
|
||||||
#define DOT_SIZE 5
|
#define DOT_SIZE 5
|
||||||
#define OBSTACLE_SIZE 50
|
|
||||||
|
|
||||||
#define STATUS_BAR_HEIGHT 30 // Height of the status bar at the bottom
|
|
||||||
|
|
||||||
|
// Define fading speed (higher = faster fading, range: 0.001 - 0.2)
|
||||||
#define FADE_SPEED 0.05f
|
#define FADE_SPEED 0.05f
|
||||||
|
|
||||||
constexpr unsigned int TRAIL_LENGTH = 20;
|
constexpr unsigned int TRAIL_LENGTH = 20;
|
||||||
|
|
||||||
// Enable debug mode
|
// Enable debug mode (set to 0 to disable debug output)
|
||||||
#define DEBUG 1
|
#define DEBUG 0
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Represents an obstacle that the walker cannot pass through.
|
* @brief Represents the walker that moves randomly on the screen.
|
||||||
*/
|
|
||||||
struct Obstacle {
|
|
||||||
Vector2 position; // Top-left corner of the obstacle
|
|
||||||
|
|
||||||
Obstacle(float x, float y) : position(x, y) {}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Checks if a given position is inside the obstacle.
|
|
||||||
*/
|
|
||||||
bool isColliding(const Vector2& pos) const {
|
|
||||||
return pos.x >= position.x && pos.x <= position.x + OBSTACLE_SIZE &&
|
|
||||||
pos.y >= position.y && pos.y <= position.y + OBSTACLE_SIZE;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Represents the walker that moves randomly.
|
|
||||||
*/
|
*/
|
||||||
struct Walker {
|
struct Walker {
|
||||||
Vector2 position;
|
Vector2 position{}; // Current position of the walker
|
||||||
Vector2 lastValidPosition;
|
|
||||||
|
|
||||||
Walker() : position(WIDTH / 2.0f, (HEIGHT - STATUS_BAR_HEIGHT) / 2.0f),
|
|
||||||
lastValidPosition(position) {}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Moves the walker in a random direction.
|
* @brief Constructor initializes the walker at the center of the screen.
|
||||||
*/
|
*/
|
||||||
void step(const std::vector<Obstacle>& obstacles) {
|
Walker() : position(WIDTH / 2.0f, HEIGHT / 2.0f) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Moves the walker one step in a random direction.
|
||||||
|
*/
|
||||||
|
void step() {
|
||||||
float angle = static_cast<float>(rand()) / RAND_MAX * 360.0f;
|
float angle = static_cast<float>(rand()) / RAND_MAX * 360.0f;
|
||||||
angle = Math::Radians(angle);
|
angle = Math::Radians(angle);
|
||||||
float dx = std::cos(angle);
|
float dx = std::cos(angle);
|
||||||
float dy = std::sin(angle);
|
float dy = std::sin(angle);
|
||||||
|
|
||||||
// Store last valid position
|
// Update position and clamp within screen boundaries
|
||||||
lastValidPosition = position;
|
|
||||||
|
|
||||||
// Update position
|
|
||||||
position.x = std::clamp(position.x + dx, 0.0f, static_cast<float>(WIDTH - 1));
|
position.x = std::clamp(position.x + dx, 0.0f, static_cast<float>(WIDTH - 1));
|
||||||
position.y = std::clamp(position.y + dy, 0.0f, static_cast<float>(HEIGHT - STATUS_BAR_HEIGHT - 1));
|
position.y = std::clamp(position.y + dy, 0.0f, static_cast<float>(HEIGHT - 1));
|
||||||
|
|
||||||
// Check for collisions
|
#if DEBUG
|
||||||
for (const auto& obstacle : obstacles) {
|
std::cout << "Walker Position: (" << position.x << ", " << position.y << ")\n";
|
||||||
if (obstacle.isColliding(position)) {
|
#endif
|
||||||
position = lastValidPosition; // Revert movement if collision occurs
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Pushes the walker away from the mouse pointer.
|
* @brief Pushes the walker away from a given (mouse) position.
|
||||||
|
* @param mouseX X coordinate of the mouse.
|
||||||
|
* @param mouseY Y coordinate of the mouse.
|
||||||
|
* @param force The force applied to push the walker away.
|
||||||
*/
|
*/
|
||||||
void pushAway(float mouseX, float mouseY, float force, const std::vector<Obstacle>& obstacles) {
|
void pushAway(float mouseX, float mouseY, float force) {
|
||||||
|
float adjustedMouseY = HEIGHT - mouseY; // Adjust for coordinate system
|
||||||
|
|
||||||
float deltaX = position.x - mouseX;
|
float deltaX = position.x - mouseX;
|
||||||
float deltaY = position.y - mouseY;
|
float deltaY = position.y - adjustedMouseY;
|
||||||
|
|
||||||
float distance = std::sqrt(deltaX * deltaX + deltaY * deltaY);
|
float distance = std::sqrt(deltaX * deltaX + deltaY * deltaY);
|
||||||
|
|
||||||
|
// Only push if the mouse is close enough
|
||||||
if (distance > 0.0f) {
|
if (distance > 0.0f) {
|
||||||
float unitX = deltaX / distance;
|
float unitX = deltaX / distance;
|
||||||
float unitY = deltaY / distance;
|
float unitY = deltaY / distance;
|
||||||
|
|
||||||
lastValidPosition = position;
|
// Apply dynamic push force
|
||||||
|
|
||||||
position.x = std::clamp(position.x + unitX * force, 0.0f, static_cast<float>(WIDTH - 1));
|
position.x = std::clamp(position.x + unitX * force, 0.0f, static_cast<float>(WIDTH - 1));
|
||||||
position.y = std::clamp(position.y + unitY * force, 0.0f, static_cast<float>(HEIGHT - STATUS_BAR_HEIGHT - 1));
|
position.y = std::clamp(position.y + unitY * force, 0.0f, static_cast<float>(HEIGHT - 1));
|
||||||
|
|
||||||
// Prevent moving into obstacles
|
|
||||||
for (const auto& obstacle : obstacles) {
|
|
||||||
if (obstacle.isColliding(position)) {
|
|
||||||
position = lastValidPosition;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -109,26 +82,28 @@ struct Walker {
|
|||||||
* @brief Resets the walker to the center of the screen.
|
* @brief Resets the walker to the center of the screen.
|
||||||
*/
|
*/
|
||||||
void resetPosition() {
|
void resetPosition() {
|
||||||
position = Vector2(WIDTH / 2.0f, (HEIGHT - STATUS_BAR_HEIGHT) / 2.0f);
|
position = Vector2(WIDTH / 2.0f, HEIGHT / 2.0f);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Custom ReWindow window class that manages the walker simulation.
|
* @brief Custom ReWindow window class that manages the random walker simulation.
|
||||||
*/
|
*/
|
||||||
class RandomWalkerWindow : public ReWindow::OpenGLWindow {
|
class RandomWalkerWindow : public ReWindow::OpenGLWindow {
|
||||||
public:
|
public:
|
||||||
|
/**
|
||||||
|
* @brief Constructor initializes the window, walker, and trail buffer.
|
||||||
|
* @param title Window title
|
||||||
|
* @param width Window width
|
||||||
|
* @param height Window height
|
||||||
|
*/
|
||||||
RandomWalkerWindow(const std::string& title, int width, int height)
|
RandomWalkerWindow(const std::string& title, int width, int height)
|
||||||
: ReWindow::OpenGLWindow(title, width, height, 2, 1),
|
: ReWindow::OpenGLWindow(title, width, height, 2, 1),
|
||||||
walker(std::make_unique<Walker>()),
|
walker(std::make_unique<Walker>()),
|
||||||
pushForce(3.0f)
|
trailBuffer{}, // Initialize trail buffer to white (1.0)
|
||||||
{
|
pushForce(3.0f) // Default push force
|
||||||
// Define four obstacles
|
|
||||||
obstacles.emplace_back(200, 150);
|
{}
|
||||||
obstacles.emplace_back(500, 150);
|
|
||||||
obstacles.emplace_back(200, 400);
|
|
||||||
obstacles.emplace_back(500, 400);
|
|
||||||
}
|
|
||||||
|
|
||||||
void OnOpen() override {
|
void OnOpen() override {
|
||||||
if (!JGL::Init({WIDTH, HEIGHT}, 0, 0))
|
if (!JGL::Init({WIDTH, HEIGHT}, 0, 0))
|
||||||
@@ -137,50 +112,66 @@ public:
|
|||||||
this->render_target = new RenderTarget({WIDTH, HEIGHT}, Colors::White);
|
this->render_target = new RenderTarget({WIDTH, HEIGHT}, Colors::White);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Called every frame to update and render the walker with a fading trail.
|
||||||
|
*/
|
||||||
void OnRefresh(float elapsed) override {
|
void OnRefresh(float elapsed) override {
|
||||||
|
// Handle user inputs
|
||||||
handleMouseInput();
|
handleMouseInput();
|
||||||
handleKeyboardInput();
|
handleKeyboardInput();
|
||||||
|
|
||||||
// Move walker
|
// Move the walker
|
||||||
walker->step(obstacles);
|
walker->step();
|
||||||
|
|
||||||
JGL::Update({WIDTH, HEIGHT});
|
JGL::Update({WIDTH, HEIGHT});
|
||||||
J2D::Begin(render_target, true);
|
J2D::Begin(render_target, true);
|
||||||
|
// Update trail (fade effect)
|
||||||
fadeTrail();
|
fadeTrail();
|
||||||
|
// Render the updated trail
|
||||||
drawTrail();
|
drawTrail();
|
||||||
drawObstacles();
|
// Update the trail buffer with the walkers current position
|
||||||
plotWalker();
|
plotWalker();
|
||||||
J2D::End();
|
J2D::End();
|
||||||
|
|
||||||
J2D::Begin(nullptr, true);
|
J2D::Begin(nullptr, true);
|
||||||
J2D::DrawRenderTarget(render_target, {0, 0});
|
J2D::DrawRenderTarget(render_target, {0, 0});
|
||||||
|
J2D::DrawString(Colors::Black, "Framerate: " + std::to_string((int) (1.f / delta_time)), 0, 0, 1, 16);
|
||||||
J2D::End();
|
J2D::End();
|
||||||
|
// Swap buffers to display the frame
|
||||||
SwapBuffers();
|
SwapBuffers();
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::unique_ptr<Walker> walker;
|
std::unique_ptr<Walker> walker;
|
||||||
std::vector<Obstacle> obstacles;
|
std::vector<std::pair<Vector2, float>> trailBuffer{}; // Stores grayscale intensity of pixels (1.0 = white, 0.0 = black)
|
||||||
std::vector<std::pair<Vector2, float>> trailBuffer{};
|
|
||||||
RenderTarget* render_target = nullptr;
|
RenderTarget* render_target = nullptr;
|
||||||
|
|
||||||
float pushForce;
|
float pushForce; // Variable push force that can be adjusted in real time
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Handles mouse input to push the walker away.
|
||||||
|
*/
|
||||||
void handleMouseInput() {
|
void handleMouseInput() {
|
||||||
auto mousePos = GetMouseCoordinates();
|
auto mousePos = GetMouseCoordinates();
|
||||||
|
|
||||||
if (IsMouseButtonDown(MouseButtons::Left)) {
|
if (IsMouseButtonDown(MouseButtons::Left)) {
|
||||||
walker->pushAway(mousePos.x, mousePos.y, pushForce, obstacles);
|
walker->pushAway(mousePos.x, mousePos.y, pushForce);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Handles keyboard input to reset the walker and adjust push force.
|
||||||
|
*/
|
||||||
void handleKeyboardInput() {
|
void handleKeyboardInput() {
|
||||||
if (IsKeyDown(Keys::Space)) {
|
if (IsKeyDown(Keys::Space)) {
|
||||||
walker->resetPosition();
|
walker->resetPosition();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (IsKeyDown(Keys::Escape)) {
|
if (IsKeyDown(Keys::Escape)) {
|
||||||
Close();
|
Close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Adjust push force using number keys 1-5
|
||||||
if (IsKeyDown(Keys::One)) pushForce = 1.0f;
|
if (IsKeyDown(Keys::One)) pushForce = 1.0f;
|
||||||
if (IsKeyDown(Keys::Two)) pushForce = 2.0f;
|
if (IsKeyDown(Keys::Two)) pushForce = 2.0f;
|
||||||
if (IsKeyDown(Keys::Three)) pushForce = 3.0f;
|
if (IsKeyDown(Keys::Three)) pushForce = 3.0f;
|
||||||
@@ -188,38 +179,39 @@ private:
|
|||||||
if (IsKeyDown(Keys::Five)) pushForce = 5.0f;
|
if (IsKeyDown(Keys::Five)) pushForce = 5.0f;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Applies the fade effect to the trail buffer.
|
||||||
|
*/
|
||||||
void fadeTrail() {
|
void fadeTrail() {
|
||||||
|
// Fade out existing trail points
|
||||||
for (auto& trailPoint : trailBuffer)
|
for (auto& trailPoint : trailBuffer)
|
||||||
trailPoint.second = std::max(trailPoint.second - FADE_SPEED, 0.0f);
|
trailPoint.second = std::max(trailPoint.second - FADE_SPEED, 0.0f);
|
||||||
|
|
||||||
trailBuffer.emplace_back(walker->position, 1.0f);
|
trailBuffer.emplace_back(walker->position, 1.0f);
|
||||||
|
|
||||||
|
// Limit trail length
|
||||||
if (trailBuffer.size() > TRAIL_LENGTH)
|
if (trailBuffer.size() > TRAIL_LENGTH)
|
||||||
trailBuffer.erase(trailBuffer.begin());
|
trailBuffer.erase(trailBuffer.begin());
|
||||||
}
|
}
|
||||||
|
|
||||||
void drawTrail() {
|
void drawTrail() {
|
||||||
for (const auto& trailPoint : trailBuffer)
|
for (const auto& trailPoint : trailBuffer)
|
||||||
J2D::DrawPoint({0, 0, 0, (uint8_t)(trailPoint.second * 255.0f)}, trailPoint.first, DOT_SIZE);
|
J2D::DrawPoint({0, 0, 0, (uint8_t) (trailPoint.second * 255.0f)}, trailPoint.first, DOT_SIZE);
|
||||||
}
|
}
|
||||||
|
|
||||||
void plotWalker() {
|
void plotWalker() {
|
||||||
J2D::DrawPoint(Colors::Black, walker->position, DOT_SIZE);
|
J2D::DrawPoint(Colors::Black, walker->position, DOT_SIZE);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Draws the four red obstacles.
|
|
||||||
*/
|
|
||||||
void drawObstacles() {
|
|
||||||
for (const auto& obstacle : obstacles) {
|
|
||||||
J2D::FillRect(Colors::Red, obstacle.position, {OBSTACLE_SIZE, OBSTACLE_SIZE});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
int main() {
|
int main() {
|
||||||
auto window = std::make_unique<RandomWalkerWindow>("Random Walker with Obstacles", WIDTH, HEIGHT);
|
auto window = std::make_unique<RandomWalkerWindow>("Random Walker with ReWindow", WIDTH, HEIGHT);
|
||||||
if (!window->Open())
|
if (!window->Open())
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
|
// Uncomment this to make things go super fast.
|
||||||
|
//window->SetVsyncEnabled(false);
|
||||||
|
|
||||||
while (!window->IsClosing()) {
|
while (!window->IsClosing()) {
|
||||||
window->ManagedRefresh();
|
window->ManagedRefresh();
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user