Panel użytkownika
Nazwa użytkownika:
Hasło:
Nie masz jeszcze konta?

[SFML] Ruch gracza w grze

Ostatnio zmodyfikowano 2024-05-11 19:55
Autor Wiadomość
tBane
Temat założony przez niniejszego użytkownika
[SFML] Ruch gracza w grze
» 2024-05-09 09:41:45
Witam.
Pracuję obecnie nad ruchem gracza w grze. Postarałem się jak najlepiej to zrobić i proszę Was o pomoc czy aby na pewno wszystko dobrze napisałem.
Oto mój kod:

C/C++
while( window->isOpen() ) {
   
sf::Event event;
   
while( window->pollEvent( event ) ) {
       
if( event.type == sf::Event::Closed )
           
 window->close();
       
       
if( event.type == sf::Event::KeyPressed ) {
           
           
if( sf::Keyboard::isKeyPressed( sf::Keyboard::Escape ) ) {
               
window->close();
               
exit( 0 );
           
}
        }
       
       
    }
// events
   
    // UPDATES
   
cout << "cursor at: " << sf::Mouse::getPosition( * window ).x << "," << sf::Mouse::getPosition( * window ).y << endl;
   
   
// Player Movement
   
if(( sf::Keyboard::isKeyPressed( sf::Keyboard::Space ) || sf::Keyboard::isKeyPressed( sf::Keyboard::Enter ) ) && player->state != states::fight ) {
       
player->attack();
   
}
   
   
if( sf::Keyboard::isKeyPressed( sf::Keyboard::W ) || sf::Keyboard::isKeyPressed( sf::Keyboard::Up ) ) {
       
if( player->direction != 0 )
           
 player->setDirection( 0 );
       
else if( !collisions( player, 0, - player->stepSize ) )
           
 player->move();
       
   
}
   
if( sf::Keyboard::isKeyPressed( sf::Keyboard::D ) || sf::Keyboard::isKeyPressed( sf::Keyboard::Right ) ) {
       
if( player->direction != 1 )
           
 player->setDirection( 1 );
       
else if( !collisions( player, player->stepSize, 0 ) )
           
 player->move();
       
   
}
   
if( sf::Keyboard::isKeyPressed( sf::Keyboard::S ) || sf::Keyboard::isKeyPressed( sf::Keyboard::Down ) ) {
       
if( player->direction != 2 )
           
 player->setDirection( 2 );
       
else if( !collisions( player, 0, player->stepSize ) )
           
 player->move();
       
   
}
   
if( sf::Keyboard::isKeyPressed( sf::Keyboard::A ) || sf::Keyboard::isKeyPressed( sf::Keyboard::Left ) ) {
       
if( player->direction != 3 )
           
 player->setDirection( 3 );
       
else if( !collisions( player, - player->stepSize, 0 ) )
           
 player->move();
       
   
}
   
   
if( player->state == states::fight && player->step == 3 ) {
       
playerAttack();
   
}
   
   
deleteDeadBeasts();
   
   
for( auto & go: gameObjects )
       
 go->update();
   
   
std::sort( gameObjects.begin(), gameObjects.end(),[ ]( const auto & a, const auto & b ) { return a->y < b->y; } );
   
   
view.setCenter( player->x, player->y );
   
   
// RENDER
   
window->clear( sf::Color( 64, 128, 64 ) );
   
window->setView( view );
   
for( auto & go: gameObjects )
       
 go->render( window );
   
   
//player->render(window);
   
window->display();
   
sf::sleep( sf::milliseconds( 70 ) );
   
} // window->isOpen();
P-181045
pekfos
» 2024-05-09 21:38:31
Podstawowe pytanie brzmi: jakie będzie zachowanie w przypadku naciśnięcia kombinacji klawiszy. 1) ruch na skos, 2) dwa przeciwne kierunki naraz. Wygląda trochę tak jakby ruch nie był wtedy w ogóle możliwy przez warunki na player->direction.

C/C++
sf::sleep( sf::milliseconds( 70 ) );
Tego zdecydowanie nie powinno tu być.
P-181047
tBane
Temat założony przez niniejszego użytkownika
» 2024-05-09 22:56:30
usunąłem sleep i wstawiłem setFramerateLimit(30).
Teraz gra działa bardzo szybko. Jak spowolnić update tzn. ruch gracza i potworków ?

Mam taką funkcję aktualizującą gracza
C/C++
void update() {
   
   
if( state == states::fight ) {
       
if( state == states::fight && step > 3 )
       
{
           
state = states::idle;
           
step = 0;
           
bodySprite->setTexture( * idleTextures[ direction * 4 ] );
       
}
       
else {
           
bodySprite->setTexture( * fightTextures[ direction * 4 + step ] );
           
step += 1;
       
}
    }
   
else if( state == states::run ) {
       
if( step > 3 )
           
 step = 0;
       
       
state = states::idle;
       
bodySprite->setTexture( * runTextures[ direction * 4 + step ] );
       
step += 1;
   
}
   
else if( state == states::idle ) {
       
if( step > 3 )
           
 step = 0;
       
       
bodySprite->setTexture( * idleTextures[ direction * 4 + step ] );
       
step += 1;
       
   
}
   
   
mouseOvering();
   
   
bodySprite->setPosition( x, y );
   
collider->setPosition( x, y );
   
attackRangeArea->setPosition( x, y );
   
textname->setPosition( x, y - height - 30 );
   
   
}
P-181048
pekfos
» 2024-05-09 23:27:08
Najlepiej nie polegać na ilości klatek na sekundę i uzależnić aktualizacje od upływu czasu rzeczywistego, ten możesz dokładnie mierzyć z użyciem sf::Clock. Mając różnicę czasu od ostatniej aktualizacji w sekundach (oznaczane często deltaT, albo dt), używasz tej wartości do aktualizowania wszystkiego. Dalej lecisz z klasycznymi wzorami z fizyki na ruch jednostajny. Gdy jako stałą w kodzie określasz prędkość, to wtedy nie ma co działać za szybko czy za wolno, o ile tylko się obliczenia zgadzają.
W przypadkach gdzie robisz inkrementację zmiennej step, też trzeba to jakoś wyrazić w oparciu o czas, na przykład z użyciem dodatkowej zmiennej float w której trzymasz czas do następnej inkrementacji.
C/C++
float countdown = 0;

void update( float dt )
{
   
countdown -= dt;
   
while( countdown <= 0 )
   
{
       
countdown += StepTimeInSeconds; // np 0.25
       
step++;
       
// Logika związana ze step
   
}
}
P-181049
tBane
Temat założony przez niniejszego użytkownika
» 2024-05-10 18:53:45
no dobra, zacząłem od dodania zmiennej upływu czasu w funkcji main.cpp. Teraz potrzebuję aktualizować gracza i nie mam pojęcia jak to zrobić.

main.cpp
C/C++
...
sf::Clock clock;
sf::Time elapsedTime = clock.getElapsedTime();
float prevTime = elapsedTime.asSeconds();
float currentTime = prevTime;
float dt;

int main() {
   
// ...
   
while( window->isOpen() ) {
       
elapsedTime = clock.getElapsedTime();
       
prevTime = currentTime;
       
currentTime = elapsedTime.asSeconds();
       
// ...
       
       
dt = currentTime - prevTime;
       
for( auto & go: gameObjects )
           
 go->update( dt );
       
       
       
// ...
        // ...
   
}
   


player.hpp
C/C++
void update( float dt ) {
   
   
float distance = 10.0f * stepSize * dt; // dodałem 10.0f, bo prędkość za mała
   
   
if( state == states::fight ) {
       
if( state == states::fight && step > 3 )
       
{
           
state = states::idle;
           
step = 0;
           
bodySprite->setTexture( * idleTextures[ direction * 4 ] );
       
}
       
else {
           
bodySprite->setTexture( * fightTextures[ direction * 4 + step ] );
           
step += 1;
       
}
    }
   
else if( state == states::run ) {
       
if( direction == 0 ) y -= distance;
       
       
if( direction == 1 ) x += distance;
       
       
if( direction == 2 ) y += distance;
       
       
if( direction == 3 ) x -= distance;
       
       
       
state = states::idle;
       
bodySprite->setTexture( * runTextures[ direction * 4 + step ] );
       
step += 1;
       
if( step > 3 )
           
 step = 0;
       
   
}
   
else if( state == states::idle ) {
       
       
bodySprite->setTexture( * idleTextures[ direction * 4 + step ] );
       
step += 1;
       
if( step > 3 )
           
 step = 0;
       
   
}
   
   
mouseOvering();
   
   
bodySprite->setPosition( x, y );
   
collider->setPosition( x, y );
   
attackRangeArea->setPosition( x, y );
   
textname->setPosition( x, y - height - 30 );
   
   
}
P-181050
tBane
Temat założony przez niniejszego użytkownika
» 2024-05-10 19:14:03
dobra, jakoś działa. Ale czy to dobrze jest napisane ?
P-181051
pekfos
» 2024-05-10 22:27:09
Ale co jest dobrze napisane? Ostatni kod był w wiadomości w której "nie miałeś pojęcia jak to zrobić". Tam aktualizacja animacji wygląda jakby polegała na częstotliwości aktualizacji a nie czasie.

C/C++
dt = currentTime - prevTime;
Odejmujesz od siebie 2 czasy liczone od startu programu jako float, więc z czasem precyzja będzie maleć i gra może przestać działać w ogóle bo dt będzie zerowe, chociaż nikt pewnie nie będzie grać tak długo by tego doświadczyć. Te odejmowanie powinno działać na sf::Time, które ma stałą precyzję. A najprościej nie pisać tego odejmowania samemu tylko użyć metody restart() zamiast getElapsedTime().
C/C++
float dt = clock.restart().asSeconds();
P-181052
tBane
Temat założony przez niniejszego użytkownika
» 2024-05-10 22:31:20
o to chodziło ?

C/C++
sf::Clock clock;
sf::Time prevTime = clock.getElapsedTime();
sf::Time currentTime = prevTime;
float dt;

//...


while( window->isOpen() ) {
   
   
prevTime = currentTime;
   
currentTime = clock.getElapsedTime();
   
   
// ...
   
   
dt = currentTime.asSeconds() - prevTime.asSeconds();
   
for( auto & go: gameObjects )
       
 go->update( dt );
   
// ...
   
P-181053
« 1 » 2
  Strona 1 z 2 Następna strona