Tworzenie gry w C++ z użyciem bibliotek SDL. Problem z ustaleniem przyspieszania i włączania gry za pomocą .exe

0

Witam. Zrobiłem grę samochodową, posługując się internetem, serwisem YouTube.
Zadeklarowane są "sprawy" związane z omijaniem przeciwników. Chciałbym dodać coś do projektu, więc postanowiłem, że będzie możliwość przyspieszania i zwalniania

np. za skręt (w lewo) odpowiada to:

 if(keystate[SDL_SCANCODE_UP])
        {
            if(velY>-25)
            {
                velY=velY-0.6;
            }
        }

I podobnie z if(keystate[SDL_SCANCODE_DOWN])

Nie trudno się domyślić, że:
if(keystate[SDL_SCANCODE_RIGHT]) - będzie aktywowany prawy przycisk.
Analogicznie do hamowania: if(keystate[SDL_SCANCODE_LEFT]). Z tym już dam radę.

Jeżeli wiem, że:
rect2.x=loop*200-(klatka*6)%200;
Odpowiada za szybkość przesuwania obrazu.
Jak to skleić, aby autko przyspieszało i zwalniało?
Wysyłam grafikę gry, aby objaśniła co nie co :)

Mam jeszcze problem innej natury;
Jeżeli włączam grę przez kompilator - działa bez zastrzeżeń. Z kolei, gdy wchodzę w:
bin -> debug -> gra o autach,exe = wywala błąd, o treści:
"nie można uruchomić programu ponieważ na komputerze nie znaleziono sdl2.dll(...)"
Jest to problematyczne, bo chciałbym np. udostępnić komuś tę grę, kto nie ma kompilatora.

0

rect2.x=loop200-(klatka6)%200; co to jest?
Cały wzór na ruch, uwzględniając prędkość, to: car.position.x += car.velocity.x; plus potem translacja z uwzględnieniem kamery, ale to dopiero podczas wyświetlania.

2

Pokusiłem się o przykład:

nagłówki
Ot, SDL, jakiś zgrabny kontener no i oczywiście dostęp do funkcji trygonometrycznych (konkretnie przydał nam się sin i cos)

#include <vector>
#include <cmath>

#include "SDL.h"
#include "SDL_image.h"
#undef main

main

auto main() {
    return finalize(entry(initialize(window_config {}))), 0;
}

Czyli tworzona jest konfiguracja okna, coś jest później nią inicjalizowane, gdzieśtam to trafia i na końcu jest sprzątanie.

initialize
Na podstawie window_config tworzone jest okno i renderer. Ponieważ nie chciało mi się bawić w liczenie delty czasu, dorzuciłem vsynca, żebym nie musiał zmyślać wartości prędkości.

auto initialize(window_config const &cfg) {
    SDL_Init(SDL_INIT_VIDEO);
    auto window = SDL_CreateWindow(cfg.title, cfg.rect.x, cfg.rect.y, cfg.rect.w, cfg.rect.h, cfg.flags);
    auto renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_PRESENTVSYNC);
    return app_context {
        window, renderer, cfg
    };
}

finalize
Czyścimy po sobie, nic wielkiego.

auto finalize(app_context &&context) {
    SDL_DestroyRenderer(context.renderer_ptr);
    SDL_DestroyWindow(context.window_ptr);
    SDL_Quit();
}

entry
czyli nasz prawdziwy main

auto entry(app_context &&context) {
    application app { context };
    while(!context.stop) {
        context.events = handle_events();
        context.stop = app.update(context);

        render(context, [&] {
            app.draw(context);
        });
    }

    return std::move(context);
}

Tworzymy jakąś aplikację dając jej app_context (o tym zaraz) i dopóki całość ma mielić:

  1. Zaciągamy zdarzenia (handle_events()) (nazwa może nie do końca trafiona, ale już nie chce mi się tego zmieniać. zaakceptuj to)
  2. Aktualizujemy stan apki
  3. rysujemy co apka chce żeby rysować

app_context

struct app_context {
    SDL_Window * const window_ptr;
    SDL_Renderer * const renderer_ptr;
    window_config cfg;
    bool stop;
    events_container events;
};

Pojawiło się kilka magicznych rzeczy do wyjaśnienia:
handle_events(), render(context, f) no i oczywiście application

handle_events()
Wyciąga zdarzenia z wewnętrznej kolejki SDLa, żebyśmy nie musieli się z nią później już bawić.

auto handle_events() {
    events_container result;
    SDL_Event event;
    while(SDL_PollEvent(&event)) {
        result.push_back(event);
    }
    return result;
}

render

template<typename F>
void render(app_context &context, F f) {
    SDL_RenderClear(context.renderer_ptr);
    f();
    SDL_RenderPresent(context.renderer_ptr);
}

Zostaje więc...
application
W perfekcyjnym świecie chciałbyś wydzielić logikę car do jakiejś ładnej struktury, która sama zajmowałaby się swoimi rzeczami - ale ponieważ jest to szybki przykład na forum to liczę, że sam się tego podejmiesz.

struct application {
    SDL_Texture * const car_image;
    float car_x = 0, car_y = 0;
    float car_angle = 50.f;
    float car_speed = 0.f;
    const float car_angle_speed = 4.f;
    const float car_acceleration = 0.4f;
    const float car_backward_acceleration = car_acceleration / 1.1f;

    application(application const &) = delete;
    auto operator=(application const &) = delete;

    application(app_context const &context): 
        car_image(IMG_LoadTexture(context.renderer_ptr, "car.png")) {}

    ~application() {
        SDL_DestroyTexture(car_image);
    }
    

    auto update(app_context const &context) {
        auto stop = consume_events(context.events, [](auto _) {});
        const Uint8 *keystate = SDL_GetKeyboardState(NULL);
        if(keystate[SDL_SCANCODE_LEFT]) {
            car_angle -= car_angle_speed;
        }
        if(keystate[SDL_SCANCODE_RIGHT]) {
            car_angle += car_angle_speed;
        }
        if(keystate[SDL_SCANCODE_UP]) {
            car_speed += car_acceleration;
        }
        if(keystate[SDL_SCANCODE_DOWN]) {
            car_speed -= car_backward_acceleration;
        }
        const double pi_divided_by_180 = 0.0174532925;
        car_x += float(car_speed*cos((double) car_angle*pi_divided_by_180));
        car_y += float(car_speed*sin((double) car_angle*pi_divided_by_180));
        return stop;
    }

    auto draw(app_context const &context) {
        draw_texture(context.renderer_ptr, car_image, SDL_Point { (int)car_x, (int)car_y }, car_angle);
    }
};

Kolejne nowe rzeczy:
consume_events, draw_texture

consume_events
Ogarnia SDL_QUIT, więc gdybyśmy chcieli męczyć jakieś zdarzenia, to możemy zająć się tylko tymi, na których nam zależy.
Początkowo tego używałem do odczytywania klawiszy, ale dla płynności ruchu przerzuciłem się na SDL_GetKeyboardState

template<typename F>
auto consume_events(events_container const &events, F f) {
    for(auto event: events) {
        if(event.type == SDL_QUIT) {
            return true;
        }
        f(event);
    }
    return false;
}

draw_texture
Ta mała dziecinka dba o to, żebyśmy nie musieli dłubać przy wyciąganiu danych z tekstury, jakiś sourcach, destinationach i innych okropnych rzeczach

auto draw_texture(SDL_Renderer *renderer, SDL_Texture *texture, SDL_Point position, float angle) {
    int w, h, x = position.x, y = position.y;
    SDL_QueryTexture(texture, nullptr, nullptr, &w, &h);
    SDL_Rect dest = { x, y, w, h };
    SDL_RenderCopyEx(renderer, texture, nullptr, &dest, angle, nullptr, SDL_FLIP_NONE);
}

Tak wygląda używane w projekcie autko:
car.png

A to przykład jazdy (nagrywane przy 20fpsach na potrzeby gifa, ofc normalnie całość jest płynna)
car_example.gif

Gdybyś chciał pojeździć, w załączniku masz builda. Przy okazji z niego możesz się dowiedzieć, że aby ktoś mógł odpalić naszą apkę, to trzeba dorzucić wszystkie niezbędne dlle. (+ dorzuciłem tam main.cpp, żebyś nie musiał sklejać w.w. snippetów)
Release.zip

1 użytkowników online, w tym zalogowanych: 0, gości: 1