adapted from noerw/experimental-shooter-no3master
@@ -34,6 +34,9 @@ namespace Proto4 { | |||
break; | |||
case GameState::Running: | |||
player.update(timestep, window.mapPixelToCoords(sf::Mouse::getPosition(window), mainView)); | |||
// mainView acts as camera. TODO: introduce lagging camera | |||
mainView.setCenter(player.getPosition()); | |||
break; | |||
default: | |||
@@ -49,6 +52,7 @@ namespace Proto4 { | |||
} | |||
bool Game::reset() { | |||
player = Player(); | |||
state = GameState::Uninitialized; | |||
return true; | |||
} |
@@ -30,6 +30,5 @@ namespace Proto4 { | |||
GameState state = GameState::Uninitialized; | |||
Player player{}; | |||
uint32_t score = 0; | |||
}; | |||
} |
@@ -99,7 +99,7 @@ namespace Proto4 { | |||
// move camera for background movement | |||
gameView.setCenter(sf::Vector2f( | |||
gameView.getCenter().x + timestep.asSeconds() * 100 * std::sin(currentTime.asSeconds()), | |||
gameView.getCenter().y - timestep.asSeconds() * 100)); | |||
gameView.getCenter().y + timestep.asSeconds() * 100)); | |||
break; | |||
case AppState::Game: | |||
state = game.update(window, timestep); |
@@ -1,4 +1,7 @@ | |||
#include <cmath> | |||
#include "player.hpp" | |||
#include "vec2util.hpp" | |||
namespace Proto4 { | |||
Player::Player() { | |||
@@ -14,4 +17,101 @@ namespace Proto4 { | |||
void Player::setPosition(sf::Vector2f pos) { | |||
triangle.setPosition(pos); | |||
} | |||
sf::Vector2f Player::getPosition() { | |||
return triangle.getPosition(); | |||
} | |||
void Player::update(sf::Time timeStep, sf::Vector2f mousePos) { | |||
setRotation(timeStep.asSeconds(), mousePos); | |||
move(timeStep.asSeconds(), keyboard2Acceleration()); | |||
} | |||
sf::Vector2f Player::keyboard2Acceleration() { | |||
sf::Vector2f acceleration = sf::Vector2f(0, 0); | |||
if ( | |||
sf::Keyboard::isKeyPressed(sf::Keyboard::Left) || | |||
sf::Keyboard::isKeyPressed(sf::Keyboard::A) | |||
) { | |||
acceleration.x -= 100.f; | |||
} | |||
if ( | |||
sf::Keyboard::isKeyPressed(sf::Keyboard::Right) || | |||
sf::Keyboard::isKeyPressed(sf::Keyboard::D) | |||
) { | |||
acceleration.x += 100.f; | |||
} | |||
if ( | |||
sf::Keyboard::isKeyPressed(sf::Keyboard::Up) || | |||
sf::Keyboard::isKeyPressed(sf::Keyboard::W) | |||
) { | |||
acceleration.y -= 100.f; | |||
} | |||
if ( | |||
sf::Keyboard::isKeyPressed(sf::Keyboard::Down) || | |||
sf::Keyboard::isKeyPressed(sf::Keyboard::S) | |||
) { | |||
acceleration.y += 100.f; | |||
} | |||
return normalized(acceleration); | |||
} | |||
void Player::move(float timeStep, sf::Vector2f accel) { | |||
accel *= acceleration * timeStep; | |||
float decel = 1 - deceleration * timeStep; | |||
// x / y accelerate & decelerate separately | |||
// to allow for exponential deceleration | |||
if (accel.y == 0.f) { | |||
speed.y *= decel; | |||
} else { | |||
speed.y += accel.y; | |||
} | |||
if (accel.x == 0.f) { | |||
speed.x *= decel; | |||
} else { | |||
speed.x += accel.x; | |||
} | |||
// normalize with speed limit * timeStep | |||
float currentSpeed = magnitude(speed); | |||
if (currentSpeed > maxSpeed) { | |||
speed *= maxSpeed / currentSpeed; | |||
} | |||
triangle.move(speed * timeStep); | |||
} | |||
void Player::setRotation(float timeStep, sf::Vector2f mousePos) { | |||
sf::Vector2f playerPos = triangle.getPosition(); | |||
if (playerPos == mousePos) return; | |||
//calc mouse<>player angle | |||
float targetRotation = -180 - atan2(mousePos.x - playerPos.x, mousePos.y - playerPos.y) * 180 / PI; | |||
float rot = triangle.getRotation(); | |||
float rotationDiff = std::remainder(targetRotation - rot + 180, 360); | |||
//limit rotationspeed | |||
if (rotationDiff > maxRotationSpeed * timeStep) { | |||
rot -= maxRotationSpeed * timeStep; | |||
} | |||
else if (rotationDiff < -maxRotationSpeed * timeStep) { | |||
rot += maxRotationSpeed * timeStep; | |||
} | |||
else { | |||
rot = targetRotation; | |||
} | |||
//bring the value back down into the interval [0, 360] | |||
if (rot < 0) { | |||
rot += 360; | |||
} | |||
else if (rot > 360) { | |||
rot -= 360; | |||
} | |||
triangle.setRotation(rot); | |||
} | |||
} |
@@ -4,16 +4,26 @@ | |||
#include <SFML/System.hpp> | |||
#include <SFML/Window.hpp> | |||
namespace Proto4 { | |||
class Player : public sf::Drawable, public sf::Transformable { | |||
sf::CircleShape triangle; | |||
sf::Vector2f speed {0.f, 0.f}; | |||
const float acceleration = 2500; | |||
const float deceleration = 3; | |||
const float maxSpeed = 650; | |||
const float maxRotationSpeed = 800; | |||
public: | |||
sf::Vector2f keyboard2Acceleration(); | |||
void move(float timeStep, sf::Vector2f acceleration); | |||
void setRotation(float timeStep, sf::Vector2f mousePos); | |||
public: | |||
Player(); | |||
virtual void draw(sf::RenderTarget &window, | |||
sf::RenderStates states) const; | |||
void update(sf::Time timeStep, sf::Vector2f mousePosition); | |||
virtual void setPosition(sf::Vector2f pos); | |||
virtual sf::Vector2f getPosition(); | |||
}; | |||
} |
@@ -0,0 +1,177 @@ | |||
// Simple utility header for the sf::Vector2f class. | |||
// copied from https://en.sfml-dev.org/forums/index.php?topic=21356.0 | |||
#pragma once | |||
#include <math.h> | |||
#include <SFML/System/Vector2.hpp> | |||
#define PI 3.14159265359 | |||
#define RAD2DEG 57.2957795056 | |||
#define DEG2RAD 0.01745329252 | |||
typedef sf::Vector2f Vec2; | |||
/** | |||
* @brief dot | |||
* @param a vector | |||
* @param b vector | |||
* @return a . b | |||
*/ | |||
static inline float dot (const Vec2& a, const Vec2& b) | |||
{ | |||
return a.x*b.x + a.y*b.y; | |||
} | |||
/** | |||
* @brief magnitudeSq | |||
* @param v vector | |||
* @return |v|^2 | |||
*/ | |||
static inline float magnitudeSq (const Vec2& v) | |||
{ | |||
return v.x*v.x + v.y*v.y; | |||
} | |||
/** | |||
* @brief magnitude | |||
* @param v vector | |||
* @return |v| | |||
*/ | |||
static inline float magnitude (const Vec2& v) | |||
{ | |||
return sqrt (v.x*v.x + v.y*v.y); | |||
} | |||
/** | |||
* @brief normalize | |||
* @param v vector | |||
* v' = v / |v| | |||
*/ | |||
static inline void normalize (Vec2& v) | |||
{ | |||
float len = sqrt (v.x*v.x + v.y*v.y); | |||
if (len != 0) { | |||
v.x = v.x / len; | |||
v.y = v.y / len; | |||
} | |||
} | |||
/** | |||
* @brief normalized (faster) | |||
* @param res vector | |||
* @param v vector | |||
* res = v / |v| | |||
*/ | |||
static inline void normalized (Vec2& res, const Vec2& v) | |||
{ | |||
float len = sqrt (v.x*v.x + v.y*v.y); | |||
if (len != 0) { | |||
res.x = v.x / len; | |||
res.y = v.y / len; | |||
} | |||
} | |||
/** | |||
* @brief normalized | |||
* @param v vector | |||
* @return res = v / |v| | |||
*/ | |||
static inline Vec2 normalized (const Vec2& v) | |||
{ | |||
Vec2 res; | |||
normalized(res, v); | |||
return res; | |||
} | |||
/** | |||
* @brief truncate (faster) | |||
* @param res vector | |||
* @param v vector | |||
* @param max magnitude | |||
* res = max * v / |v| | |||
*/ | |||
static inline void truncate (Vec2& res, const Vec2& v, float max) | |||
{ | |||
float len = sqrt (v.x*v.x + v.y*v.y); | |||
if (len > max) | |||
{ | |||
res = normalized(v); | |||
res *= max; | |||
} else { | |||
res = v; | |||
} | |||
} | |||
/** | |||
* @brief truncate | |||
* @param v vector | |||
* @param max magnitude | |||
* @return res = max * v / |v| | |||
*/ | |||
static inline Vec2 truncate (const Vec2& v, float max) | |||
{ | |||
Vec2 res; | |||
truncate(res, v, max); | |||
return res; | |||
} | |||
/** | |||
* @brief lerp (faster) | |||
* @param res vector | |||
* @param a vector | |||
* @param b vector | |||
* @param t alpha value | |||
* res = (1-t)*a + t*b | |||
*/ | |||
static inline void lerp (Vec2& res, const Vec2& a, const Vec2& b, float t) | |||
{ | |||
res.x = (1-t)*a.x + t*b.x; | |||
res.y = (1-t)*a.y + t*b.y; | |||
} | |||
/** | |||
* @brief lerp | |||
* @param a vector | |||
* @param b vector | |||
* @param t alpha value | |||
* @return res = (1-t)*a + t*b | |||
*/ | |||
static inline Vec2 lerp (const Vec2& a, const Vec2& b, float t) | |||
{ | |||
Vec2 res; | |||
lerp(res, a, b, t); | |||
return res; | |||
} | |||
/** | |||
* @brief rotate | |||
* @param res vector | |||
* @param v vector | |||
* @param angle | |||
* R = |cos -sin| | |||
* |sin cos| | |||
* res = v * R | |||
*/ | |||
static inline void rotate (Vec2& res, const Vec2& v, float angle) | |||
{ | |||
const float rad = DEG2RAD * angle; | |||
const float sin0 = sin(rad); | |||
const float cos0 = cos(rad); | |||
res.x = v.x * cos0 + v.y * -sin0; | |||
res.y = v.x * sin0 + v.y * cos0; | |||
} | |||
/** | |||
* @brief rotate | |||
* @param v vector | |||
* @param angle | |||
* @return res = v * R | |||
*/ | |||
static inline Vec2 rotate (Vec2& v, float angle) | |||
{ | |||
Vec2 res; | |||
rotate(res, v, angle); | |||
return res; | |||
} |