Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
Binary file added assets/images/eye_lid.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/images/snake_mouth.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
60 changes: 60 additions & 0 deletions lib/include/chomper/animations/head_animation.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
#pragma once
#include "chomper/animator.hpp"
#include "chomper/controller.hpp"
#include "klib/base_types.hpp"
#include "klib/log.hpp"
#include "klib/ptr.hpp"
#include "le2d/drawable/sprite.hpp"
#include "le2d/render_instance.hpp"
#include <glm/vec2.hpp>

namespace chomper {
class Engine;

namespace animation {
class DirectionProvider : public klib::Polymorphic {
public:
[[nodiscard]] virtual glm::vec2 getHeadPosition() const = 0;
[[nodiscard]] virtual Heading getHeading() const = 0;
};

class CollectibleProvider : public klib::Polymorphic {
public:
[[nodiscard]] virtual std::span<le::RenderInstance const> getCollectibles() const = 0;
};

class Eye {
public:
explicit Eye(le::ITexture& eyeLidTexture);
void setPosition(glm::vec2 position);
void movePupil(glm::vec2 target);
void draw(le::IRenderer& renderer, bool drawEyeLids) const;

private:
le::drawable::Sprite m_eyeLid{};
le::drawable::Circle m_eye{};
le::drawable::Circle m_pupil{};
};

class HeadAnimation : public IAnimation {
public:
explicit HeadAnimation(klib::Ptr<DirectionProvider const> directionProvider, klib::Ptr<CollectibleProvider const> collectibleProvider,
Comment thread
stanplayzz marked this conversation as resolved.
Outdated
gsl::not_null<Engine const*> engine);
void tick(kvf::Seconds dt) final;
void draw(le::IRenderer& renderer) const final;

private:
klib::TypedLogger<HeadAnimation> m_log;

klib::Ptr<DirectionProvider const> m_directionProvider{};
klib::Ptr<CollectibleProvider const> m_collectibleProvider{};
Comment thread
stanplayzz marked this conversation as resolved.
Outdated

le::drawable::Sprite m_mouth{};
std::unique_ptr<Eye> m_leftEye{};
std::unique_ptr<Eye> m_rightEye{};
klib::Ptr<le::ITexture> m_eyeLidTexture{};

bool m_shouldDraw{};
};
} // namespace animation
} // namespace chomper
1 change: 1 addition & 0 deletions lib/include/chomper/animator.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ class Animator {
void play(std::unique_ptr<IAnimation> animation);

void tick(kvf::Seconds dt);
void stopAll();

void draw(le::IRenderer& renderer) const;

Expand Down
18 changes: 16 additions & 2 deletions lib/include/chomper/player.hpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#pragma once
#include "chomper/animations/head_animation.hpp"
#include "chomper/animator.hpp"
#include "chomper/controller.hpp"
#include "chomper/debug_inspector.hpp"
Expand All @@ -14,14 +15,18 @@
namespace chomper {
class Engine;

class Player : public IController::IListener, public IDebugInspector, public klib::Pinned {
namespace runtime {
class Game;
}

class Player : public IController::IListener, public IDebugInspector, public klib::Pinned, public animation::DirectionProvider {
public:
struct Info {
bool alive = true;
int score{};
};

explicit Player(le::input::ScopedActionMapping& mapping, gsl::not_null<Engine const*> engine);
explicit Player(le::input::ScopedActionMapping& mapping, gsl::not_null<Engine const*> engine, gsl::not_null<runtime::Game const*> game);
Comment thread
stanplayzz marked this conversation as resolved.
Outdated

void tick(kvf::Seconds dt);
void draw(le::IRenderer& renderer) const;
Expand All @@ -35,6 +40,15 @@ class Player : public IController::IListener, public IDebugInspector, public kli
return m_snake.getSegments();
}

[[nodiscard]] glm::vec2 getHeadPosition() const final {
KLIB_ASSERT(!m_snake.getSegments().empty());
return m_snake.getSegments().back().transform.position;
}

[[nodiscard]] Heading getHeading() const final {
return m_heading;
}

private:
[[nodiscard]] bool isCollidingWithSelf(glm::ivec2 targetGrid) const;
[[nodiscard]] bool isCollidingWithWall(glm::ivec2 targetGrid) const;
Expand Down
7 changes: 6 additions & 1 deletion lib/include/chomper/runtimes/game.hpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#pragma once
#include "chomper/animations/head_animation.hpp"
#include "chomper/collectibles.hpp"
#include "chomper/engine.hpp"
#include "chomper/player.hpp"
Expand All @@ -15,10 +16,14 @@

namespace chomper::runtime {
// driven by Engine, owner (whether indirectly) of all game things.
class Game : public IRuntime, public klib::Pinned {
class Game : public IRuntime, public klib::Pinned, public animation::CollectibleProvider {
public:
explicit Game(gsl::not_null<Engine*> engine);

[[nodiscard]] std::span<le::RenderInstance const> getCollectibles() const final {
return m_collectibles->getInstances();
}

private:
// all Game-level input actions.
struct Actions {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#include "chomper/animations/deathAnimation.hpp"
#include "chomper/animations/death_animation.hpp"
#include "chomper/world_space.hpp"
#include <numbers>

Expand Down
107 changes: 107 additions & 0 deletions lib/src/animations/head_animation.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
#include "chomper/animations/head_animation.hpp"
#include "chomper/engine.hpp"
#include "chomper/world_space.hpp"
#include "klib/enum_array.hpp"

namespace chomper::animation {
namespace {
constexpr klib::EnumArray<Heading, float> headingToRot_v{glm::radians(90.f), glm::radians(0.f), glm::radians(270.f), glm::radians(180.f)};
Comment thread
stanplayzz marked this conversation as resolved.
Outdated

constexpr auto isAdjacent(glm::ivec2 const& grid1, glm::ivec2 const& grid2) {
auto dx = std::abs(grid1.x - grid2.x);
auto dy = std::abs(grid1.y - grid2.y);

return (dx <= 1 && dy <= 1) && (dx != 0 || dy != 0);
}

std::optional<glm::vec2> findAdjacentCollectible(std::span<le::RenderInstance const> collectibles, glm::ivec2 headPos) {
auto it = std::ranges::find_if(collectibles, [&](auto const& c) {
return isAdjacent(headPos, worldSpace::worldToGrid(c.transform.position));
});
if (it == collectibles.end()) {
return std::nullopt;
}
return it->transform.position;
}
Comment thread
stanplayzz marked this conversation as resolved.

} // namespace

HeadAnimation::HeadAnimation(klib::Ptr<DirectionProvider const> directionProvider, klib::Ptr<CollectibleProvider const> collectibleProvider,
gsl::not_null<Engine const*> engine)
: m_directionProvider(directionProvider), m_collectibleProvider(collectibleProvider) {
m_mouth.set_base_size(tileSize_v);
m_mouth.set_texture(engine->getResources().load<le::ITexture>("images/snake_mouth.png"));

m_eyeLidTexture = engine->getResources().load<le::ITexture>("images/eye_lid.png");
m_leftEye = std::make_unique<Eye>(*m_eyeLidTexture);
m_rightEye = std::make_unique<Eye>(*m_eyeLidTexture);
Comment thread
stanplayzz marked this conversation as resolved.
}

void HeadAnimation::tick(kvf::Seconds /*dt*/) {
auto headPos = m_directionProvider->getHeadPosition();
auto heading = m_directionProvider->getHeading();

auto c = std::cos(headingToRot_v[heading]);
auto s = std::sin(headingToRot_v[heading]);
auto rotation = glm::mat2(c, -s, s, c);

auto rightEyeOffset = rotation * glm::vec2{tileSize_v.x * 0.22f, tileSize_v.y * -0.22f};
m_rightEye->setPosition(headPos + rightEyeOffset);

auto leftEyeOffset = rotation * glm::vec2{tileSize_v.x * -0.22f, tileSize_v.y * -0.22f};
m_leftEye->setPosition(headPos + leftEyeOffset);

auto target = findAdjacentCollectible(m_collectibleProvider->getCollectibles(), worldSpace::worldToGrid(headPos));
if (!target) {
m_shouldDraw = false;
return;
}
m_shouldDraw = true;
Comment thread
karnkaul marked this conversation as resolved.

auto mouthOffset = rotation * glm::vec2{0.f, tileSize_v.x * 0.5f};
m_mouth.transform.position = headPos + mouthOffset;
m_mouth.transform.orientation = le::nvec2::from_radians(headingToRot_v[heading]);

m_leftEye->movePupil(*target);
m_rightEye->movePupil(*target);
}

void HeadAnimation::draw(le::IRenderer& renderer) const {
m_leftEye->draw(renderer, !m_shouldDraw);
m_rightEye->draw(renderer, !m_shouldDraw);

if (!m_shouldDraw) {
return;
}
m_mouth.draw(renderer);
}

Eye::Eye(le::ITexture& texture) {
m_eye.create(tileSize_v.x * 0.4f);
m_pupil.create(m_eye.get_diameter() * 0.5f);
m_pupil.tint = kvf::Color{glm::vec4{0.f, 0.f, 0.f, 1.f}};
m_eyeLid.set_base_size(m_eye.get_size());

m_eyeLid.set_texture(&texture);
}

void Eye::setPosition(glm::vec2 position) {
m_eyeLid.transform.position = position;
m_eye.transform.position = position;
m_pupil.transform.position = position;
}

void Eye::movePupil(glm::vec2 target) {
auto dir = glm::normalize(target - m_eye.transform.position);
m_pupil.transform.position = m_eye.transform.position + dir * m_eye.get_diameter() * 0.25f;
}

void Eye::draw(le::IRenderer& renderer, bool drawEyeLids) const {
m_eye.draw(renderer);
m_pupil.draw(renderer);
if (drawEyeLids) {
m_eyeLid.draw(renderer);
}
}

} // namespace chomper::animation
4 changes: 4 additions & 0 deletions lib/src/animator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ void Animator::tick(kvf::Seconds dt) {
}
}

void Animator::stopAll() {
m_playing.clear();
}

void Animator::draw(le::IRenderer& renderer) const {
for (auto const& animation : m_playing) {
animation->draw(renderer);
Expand Down
8 changes: 6 additions & 2 deletions lib/src/player.cpp
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
#include "chomper/player.hpp"
#include "chomper/animations/deathAnimation.hpp"
#include "chomper/animations/death_animation.hpp"
#include "chomper/controllers/player_controller.hpp"
#include "chomper/engine.hpp"
#include "chomper/runtimes/game.hpp"
#include "chomper/world_size.hpp"
#include "chomper/world_space.hpp"
#include <algorithm>
Expand All @@ -13,9 +14,11 @@ constexpr auto oppositeHeading_v = klib::EnumArray<Heading, Heading>{Heading::We
constexpr auto headingToDir_v = klib::EnumArray<Heading, glm::ivec2>{glm::ivec2{1, 0}, glm::ivec2{0, 1}, glm::ivec2{-1, 0}, glm::ivec2{0, -1}};
} // namespace

Player::Player(le::input::ScopedActionMapping& mapping, gsl::not_null<Engine const*> engine) : m_engine(engine) {
Player::Player(le::input::ScopedActionMapping& mapping, gsl::not_null<Engine const*> engine, gsl::not_null<runtime::Game const*> game) : m_engine(engine) {
createController(mapping);
updateScoreText();

m_animator.play(std::make_unique<animation::HeadAnimation>(this, game, engine));
}

void Player::tick(kvf::Seconds dt) {
Expand Down Expand Up @@ -71,6 +74,7 @@ void Player::move() {
if (isCollidingWithSelf(targetGrid) || isCollidingWithWall(targetGrid)) {
if (m_graceMove) {
m_info.alive = false;
m_animator.stopAll();
m_animator.play(std::make_unique<animation::DeathAnimation>(m_snake.getSegments()));
} else {
m_graceMove = true;
Expand Down
2 changes: 1 addition & 1 deletion lib/src/runtimes/game.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ void Game::createPlayer() {
// rebind game actions.
bindActions();
// create the player, passing a reference of the logger and a reference of the input mapping to create its PlayerController.
m_player = std::make_unique<Player>(m_mapping, m_engine);
m_player = std::make_unique<Player>(m_mapping, m_engine, this);
}

void Game::createCollectibles() {
Expand Down
Loading