SFML community forums

Help => General => Topic started by: juicemaker9000 on August 15, 2021, 04:14:13 pm

Title: Ant simulation project not working
Post by: juicemaker9000 on August 15, 2021, 04:14:13 pm
Hi,
I'm working on an ant simulation project for school. I'm trying to recreate this youtube video
https://youtu.be/X-iSQQgOd1A?t=329 (https://youtu.be/X-iSQQgOd1A?t=329). At the moment my ant goes in one direction and never changes steering whatever variable I change (maxSpeed, steerStrength, wanderStrength, etc.)... It's my first SFML project so I don't know if I got something wrong in the ant movement logic or wrong use of SFML methods. Any help is appreciated! Thanks.
(I attached theresoruce files if anyone wants to try.)
Here is the code:
#include <SFML/Graphics.hpp>
#include <vector>
#include <iostream>
#include <math.h>
#include <Windows.h>
#define M_PI  3.141592653
#define rad2deg 360 / (M_PI * 2)
sf::Vector2f position, velocity, desiredDirection, oneandone(1.0f, 1.0f);
int ct = 0;
float sqrMagnitudeFunc(sf::Vector2f vector){
    return vector.x * vector.x + vector.y * vector.y;
}

sf::Vector2f ClampMagnitude(sf::Vector2f vector, float maxLength)
{
    float sqrMagnitude = sqrMagnitudeFunc(vector);
    if (sqrMagnitude > maxLength * maxLength)
    {
        float mag = (float)sqrt(sqrMagnitude);

        float normalized_x = vector.x / mag;
        float normalized_y = vector.y / mag;
        return sf::Vector2f(normalized_x * maxLength,
            normalized_y * maxLength);
    }
    return vector;
}

sf::Vector2f normalize(const sf::Vector2f &source){
    float length = sqrt((source.x * source.x) + (source.y * source.y));
    if (length != 0)
        return sf::Vector2f(source.x / length, source.y / length);
    else
        return source;
}

sf::Vector2f randPoint(float rad, float xc, float yc) {
    //srand(420);
    float ang, hyp, adj, opp;
    ang = (float)rand() / RAND_MAX * 2 * M_PI;
    hyp = sqrt((float)rand() / RAND_MAX) * rad;
    adj = cos(ang) * hyp;
    opp = sin(ang) * hyp;
    sf::Vector2f v(xc + adj, yc + opp);
    return v;
}

void draw(std::vector<sf::Sprite> &shapes, sf::RenderWindow &window) {
    for (auto s : shapes) {
        window.draw(s);
        //std::cout << s.getPosition().x << "  " << s.getPosition().y << std::endl;
    }
}

void move(std::vector<sf::Sprite> &shapes, sf::Time dt, sf::RenderWindow &window) {
   
    float maxSpeed = 0.5;
    float steerStrength = 2;
    float wanderStrength = 1;
    std::cout << ct << std::endl;
    for (auto& s : shapes) {
        desiredDirection = normalize(desiredDirection + randPoint(1.0, 0.0, 0.0) * wanderStrength);
       
        sf::Vector2f desiredVelocity = desiredDirection * maxSpeed;
        sf::Vector2f desiredSteeringForce = (desiredVelocity - velocity) * steerStrength;
        sf::Vector2f acceleration = ClampMagnitude(desiredSteeringForce, steerStrength);
        //std::cout << desiredDirection.x << " direction " << desiredDirection.y << std::endl;
        //std::cout << acceleration.x << " acc " << acceleration.y << std::endl;
        velocity = ClampMagnitude(velocity + acceleration * dt.asSeconds(), maxSpeed);
        //std::cout << dt.asSeconds()  << "delta" << std::endl;
        position += velocity * dt.asSeconds();
        //std::cout << velocity.x <<"  "<<velocity.y << std::endl;
        float angle = (float)atan2(velocity.y, velocity.x) * (float)rad2deg;
        s.setPosition(position.x  + s.getPosition().x, position.y  + s.getPosition().y);
        s.setRotation(angle + 90);
        std::cout << position.x << "  " << position.y << "  " <<angle << std::endl;
    }
}

int main()
{
    sf::RenderWindow window(sf::VideoMode(1280, 720), "Mravi9000");
   
    std::vector<sf::Sprite> shapes;

    //mravinjak
    sf::Texture tMravinjak;
    if (!tMravinjak.loadFromFile("res/marker.png"))
        return -1;
    tMravinjak.setSmooth(true);
    sf::Sprite sMravinjak;
    sMravinjak.setTexture(tMravinjak);
    sMravinjak.setScale(1.5f, 1.5f);
    sMravinjak.setColor(sf::Color(165, 42, 42));
    sMravinjak.setPosition(1280/2 - tMravinjak.getSize().x / 2 * 1.5f, 720 / 2 - tMravinjak.getSize().y / 2 * 1.5f);
   
    //mrav
    sf::Texture tMrav;
    if (!tMrav.loadFromFile("res/ant_2.png"))
        return -1;
    tMrav.setSmooth(true);
    sf::Sprite sMrav;
    sMrav.setTexture(tMrav);
    sMrav.setScale(0.2f, 0.2f);
    sMrav.setColor(sf::Color(255, 0, 0));
    sMrav.setPosition(1280 / 2 - tMrav.getSize().x / 2 * 0.2f, 720 / 2 - tMrav.getSize().y / 2 * 0.2f);
    sMrav.setOrigin(tMrav.getSize().x / 2 * 0.2f, tMrav.getSize().y / 2 * 0.2f);
    std::cout << 1280 / 2 - tMrav.getSize().x / 2 * 0.2f << "  " << 720 / 2 - tMrav.getSize().y / 2 * 0.2f << std::endl;
    shapes.push_back(sMrav);
    sf::Vector2f v;
    sf::Clock deltaClock;
    while (window.isOpen()){
        sf::Event event;
        while (window.pollEvent(event)){
            if (event.type == sf::Event::Closed)
                window.close();
        }

        //clear
        window.clear(sf::Color(49,99,0));
        window.draw(sMravinjak);
        //moving
        move(shapes, deltaClock.getElapsedTime(), window);
        draw(shapes, window);
        v = randPoint(1.0, 0.0, 0.0);
        //std::cout << v.x << " " << v.y << std::endl;
        //display
        window.display();
        deltaClock.restart();
    }

    return 0;
}
 
Title: Re: Ant simulation project not working
Post by: eXpl0it3r on August 19, 2021, 08:21:14 am
The only thing that's currently influencing the direction is wanderStrength, but that is constraint by whatever randPoint returns.
My suggestion is to print these values, that way you see how they change. Maybe randPoint is simply not providing a large enough change to notice a direction change.
Title: Re: Ant simulation project not working
Post by: kojack on August 19, 2021, 12:18:36 pm
I had a quick run of the code, but have been busy with work so haven't gotten it working as I'm guessing it should.
But a few quick notes:
- The general accumulation looks right, force into acceleration, acceleration into velocity, velocity into position. But then position is added to sprite position, so position is acting like another velocity. I'd leave out the GetPosition+position part and set the sprite to just position (giving position the actual sprite pos at the start), or add velocity to GetPosition.
- The velocity starts off small, then quickly converges on microscopic. After a few seconds of running it had values like 0.04, those are in pixels per second, so it would take 25 seconds to move a pixel. It's enough to turn the ant, but not enough to move.
- The reason it drifts is the position variable has velocity added a few times while velocity is big, then as velocity shrinks it has less effect on position and can't overcome the values gathered from the initial few frames. Then position is added to sprite GetPosition, which moves it by that much.
- maxSpeed of 0.5 means 2 seconds to move a pixel.

I think the velocity is going very small because the wandering is a bit too random. Too many large random direction changes will average out.