Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions docs/USAGE.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,15 @@ engine.setAction(velos::Key::W, "MoveForward", velos::ActionType::Hold, 0.05f, [
engine.camera()->transform().position.x += 50.f;
engine.camera()->setZoom(engine.camera()->zoom() * 1.001f);
```
- Attach to an entity and follow it:
```cpp
engine.attachCamera(*entity);
```
- Configure camera mode and its parameters:
```cpp
engine.camera()->setFollowMode(velos::CameraFollowMode::Smooth);
engine.camera()->setLookAhead(80.f);
```

## Rendering
- Shapes added to entities are automatically rendered
Expand Down
3 changes: 3 additions & 0 deletions engine/include/velos/app/engine.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ namespace velos {
void setAction(Key key, const ActionId& action, ActionType type, float holdThreshold, std::function<void(float)> callback);
void processActions(float dt);

void attachCamera(Entity& entity);
void detachCamera();

static Engine* instance() { return s_instance; }
Camera* camera() { return m_camera.get(); }
EntityManager* entityManager() { return m_entityManager.get(); }
Expand Down
25 changes: 25 additions & 0 deletions engine/include/velos/graphics/camera/camera.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#pragma once
#include <algorithm>
#include "camera_mode.h"
#include "../../math/transform.h"

namespace velos {
Expand All @@ -9,6 +10,18 @@ namespace velos {

Transform& transform() { return m_transform; }

void follow(const Transform* target);
void unfollow();
bool isFollowing();

void setFollowMode(CameraFollowMode mode);
void setSmoothSpeed(float speed);

void setLookAhead(float distance);
void setLookAheadSmoothing(float speed);

void update(float dt);

float zoom() { return m_zoom; }
void setZoom(float zoom) { m_zoom = std::max(0.01f, zoom); }

Expand All @@ -17,6 +30,18 @@ namespace velos {

private:
Transform m_transform;
const Transform* m_target = nullptr;

CameraFollowMode m_mode = CameraFollowMode::Instant;
float m_smoothSpeed = 8.f;

float m_lookAheadDistance = 0.f;
float m_lookAheadSmoothing = 6.f;
Vec2 m_currentLookAhead{0.f, 0.f};

Vec2 m_lastTargetPos{0.f, 0.f};

Vec2 m_offset{0.f, 0.f};
float m_zoom = 1.f;
};
}
8 changes: 8 additions & 0 deletions engine/include/velos/graphics/camera/camera_mode.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
#pragma once

namespace velos {
enum class CameraFollowMode {
Instant,
Smooth
};
}
8 changes: 8 additions & 0 deletions engine/include/velos/math/math.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
#pragma once
#include "transform.h"

namespace velos {
static Vec2 lerp(const Vec2& a, const Vec2& b, float t) {
return a + (b - a) * t;
}
}
23 changes: 23 additions & 0 deletions engine/include/velos/math/transform.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,29 @@ namespace velos {
Vec2 operator/(float s) const {
return { x / s, y / s };
}

float length() const {
return std::sqrt(x * x + y * y);
}

float lengthSquared() const {
return x * x + y * y;
}

Vec2 normalized() const {
float len = length();
if (len < 0.00001f)
return {0.f, 0.f};
return {x / len, y / len};
}

void normalize() {
float len = length();
if (len < 0.00001f)
return;
x /= len;
y /= len;
}
};

class Transform {
Expand Down
9 changes: 9 additions & 0 deletions engine/src/app/engine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ namespace velos {
processActions(dt);

m_entityManager->update(dt);
m_camera->update(dt);

m_renderer->beginFrame();
m_entityManager->render(*m_renderer);
Expand Down Expand Up @@ -92,4 +93,12 @@ namespace velos {
return;
}
}

void Engine::attachCamera(Entity& entity) {
m_camera->follow(&entity.transform());
}

void Engine::detachCamera() {
m_camera->unfollow();
}
}
56 changes: 56 additions & 0 deletions engine/src/graphics/camera/camera.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,62 @@
#include "velos/graphics/camera/camera.h"
#include <cmath>
#include "velos/math/math.h"

namespace velos {
void Camera::follow(const Transform* target) {
m_target = target;
}

void Camera::unfollow() {
m_target = nullptr;
}

bool Camera::isFollowing() {
return m_target != nullptr;
}

void Camera::setFollowMode(CameraFollowMode mode) {
m_mode = mode;
}

void Camera::setSmoothSpeed(float speed) {
m_smoothSpeed = speed;
}

void Camera::setLookAhead(float distance) {
m_lookAheadDistance = distance;
}

void Camera::setLookAheadSmoothing(float speed) {
m_lookAheadSmoothing = speed;
}

void Camera::update(float dt) {
if (!m_target)
return;

Vec2 delta = m_target->position - m_lastTargetPos;
m_lastTargetPos = m_target->position;
Vec2 desiredLookAhead{0.f, 0.f};

if (delta.lengthSquared() > 0.001f) {
Vec2 dir = delta.normalized();
desiredLookAhead = dir * m_lookAheadDistance;
}

float lookT = 1.f - std::exp(-m_lookAheadSmoothing * dt);
m_currentLookAhead = lerp(m_currentLookAhead, desiredLookAhead, lookT);

Vec2 desiredPos = m_target->position + m_offset + m_currentLookAhead;

if (m_mode == CameraFollowMode::Instant) {
m_transform.position = m_target->position + m_offset;
} else {
float t = 1.f - std::exp(-m_smoothSpeed * dt);
m_transform.position = lerp(m_transform.position, desiredPos, t);
}
}

Vec2 Camera::worldToScreen(const Vec2& world, const Vec2& screenCenter) const {
Vec2 relative = world - m_transform.position;
return screenCenter + relative * m_zoom;
Expand Down
8 changes: 8 additions & 0 deletions examples/sandbox/src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,15 @@ int main() {

velos::Vec2 size{50.f, 50.f};
auto rect = std::make_shared<velos::Rectangle>(velos::Vec2{0.f, 0.f} - size / 2.f, size, velos::Color{255, 0, 0});
auto circle = std::make_shared<velos::Circle>(velos::Vec2{100.f, 100.f}, 50, velos::Color{255, 255, 0});

auto entity = std::make_shared<velos::Entity>();
entity->addShape(rect);
entity->transform().position = {200, 200};

auto stationary_entity = std::make_shared<velos::Entity>();
stationary_entity->addShape(circle);

float speed = 200.f;
engine.setAction(velos::Key::W, "MoveForward", velos::ActionType::Hold, 0.05f, [entity, speed](float dt){ entity->move(0, -speed * dt); });
engine.setAction(velos::Key::S, "MoveBackward", velos::ActionType::Hold, 0.05f, [entity, speed](float dt){ entity->move(0, speed * dt); });
Expand Down Expand Up @@ -60,6 +64,10 @@ int main() {
engine.setAction(velos::Key::Space, "Charge", velos::ActionType::HoldCompleted, 1.f, [](float dt){ VELOS_INFO("Charged!"); });

engine.entityManager()->addEntity(entity);
engine.entityManager()->addEntity(stationary_entity);

engine.attachCamera(*entity);
engine.camera()->setFollowMode(velos::CameraFollowMode::Smooth);

engine.run();
}
Loading