mirror of
https://github.com/NoelFB/tiny_link.git
synced 2024-11-28 19:08:55 +08:00
room transition + attack anim
This commit is contained in:
parent
a8b78214f9
commit
fb9cebd8ff
Binary file not shown.
Before Width: | Height: | Size: 129 B After Width: | Height: | Size: 168 B |
BIN
content/map/1x0.png
Normal file
BIN
content/map/1x0.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 130 B |
Binary file not shown.
Binary file not shown.
BIN
content/tilesets/grass.ase
Normal file
BIN
content/tilesets/grass.ase
Normal file
Binary file not shown.
BIN
content/tilesets/plants.ase
Normal file
BIN
content/tilesets/plants.ase
Normal file
Binary file not shown.
|
@ -0,0 +1,11 @@
|
||||||
|
#include "sprite.h"
|
||||||
|
|
||||||
|
using namespace TL;
|
||||||
|
|
||||||
|
const Sprite::Animation* TL::Sprite::get_animation(const String& name) const
|
||||||
|
{
|
||||||
|
for (auto& it : animations)
|
||||||
|
if (it.name == name)
|
||||||
|
return ⁢
|
||||||
|
return nullptr;
|
||||||
|
}
|
|
@ -17,10 +17,20 @@ namespace TL
|
||||||
{
|
{
|
||||||
String name;
|
String name;
|
||||||
Vector<Frame> frames;
|
Vector<Frame> frames;
|
||||||
|
|
||||||
|
float duration() const
|
||||||
|
{
|
||||||
|
float d = 0;
|
||||||
|
for (auto& it : frames)
|
||||||
|
d += it.duration;
|
||||||
|
return d;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
String name;
|
String name;
|
||||||
Vec2 origin;
|
Vec2 origin;
|
||||||
Vector<Animation> animations;
|
Vector<Animation> animations;
|
||||||
|
|
||||||
|
const Animation* get_animation(const String& name) const;
|
||||||
};
|
};
|
||||||
}
|
}
|
|
@ -9,7 +9,12 @@ Animator::Animator(const String& sprite)
|
||||||
m_animation_index = 0;
|
m_animation_index = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Animator::play(const String& animation)
|
const Sprite* Animator::sprite() const
|
||||||
|
{
|
||||||
|
return m_sprite;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Animator::play(const String& animation, bool restart)
|
||||||
{
|
{
|
||||||
BLAH_ASSERT(m_sprite, "No Sprite Assigned!");
|
BLAH_ASSERT(m_sprite, "No Sprite Assigned!");
|
||||||
|
|
||||||
|
@ -17,8 +22,13 @@ void Animator::play(const String& animation)
|
||||||
{
|
{
|
||||||
if (m_sprite->animations[i].name == animation)
|
if (m_sprite->animations[i].name == animation)
|
||||||
{
|
{
|
||||||
m_animation_index = i;
|
if (m_animation_index != i || restart)
|
||||||
m_frame_index = 0;
|
{
|
||||||
|
m_animation_index = i;
|
||||||
|
m_frame_index = 0;
|
||||||
|
m_frame_counter = 0;
|
||||||
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,8 +21,8 @@ namespace TL
|
||||||
Animator() = default;
|
Animator() = default;
|
||||||
Animator(const String& sprite);
|
Animator(const String& sprite);
|
||||||
|
|
||||||
void play(const String& animation);
|
const Sprite* sprite() const;
|
||||||
|
void play(const String& animation, bool restart = false);
|
||||||
void update() override;
|
void update() override;
|
||||||
void render(Batch& batch) override;
|
void render(Batch& batch) override;
|
||||||
|
|
||||||
|
|
|
@ -27,12 +27,18 @@ Player::Player()
|
||||||
.press_buffer(0.15f)
|
.press_buffer(0.15f)
|
||||||
.add_key(Key::X)
|
.add_key(Key::X)
|
||||||
.add_button(0, Button::A);
|
.add_button(0, Button::A);
|
||||||
|
|
||||||
|
input_attack = VirtualButton()
|
||||||
|
.press_buffer(0.15f)
|
||||||
|
.add_key(Key::C)
|
||||||
|
.add_button(0, Button::X);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Player::update()
|
void Player::update()
|
||||||
{
|
{
|
||||||
input_move.update();
|
input_move.update();
|
||||||
input_jump.update();
|
input_jump.update();
|
||||||
|
input_attack.update();
|
||||||
|
|
||||||
auto mover = get<Mover>();
|
auto mover = get<Mover>();
|
||||||
auto anim = get<Animator>();
|
auto anim = get<Animator>();
|
||||||
|
@ -44,37 +50,99 @@ void Player::update()
|
||||||
{
|
{
|
||||||
// land squish
|
// land squish
|
||||||
if (!was_on_ground && m_on_ground)
|
if (!was_on_ground && m_on_ground)
|
||||||
anim->scale = Vec2(facing * 1.5f, 0.7f);
|
anim->scale = Vec2(m_facing * 1.5f, 0.7f);
|
||||||
|
|
||||||
// lerp scale back to one
|
// lerp scale back to one
|
||||||
anim->scale = Calc::approach(anim->scale, Vec2(facing, 1.0f), Time::delta * 4);
|
anim->scale = Calc::approach(anim->scale, Vec2(m_facing, 1.0f), Time::delta * 4);
|
||||||
|
|
||||||
|
// set m_facing
|
||||||
|
anim->scale.x = Calc::abs(anim->scale.x) * m_facing;
|
||||||
|
|
||||||
// set facing
|
|
||||||
anim->scale.x = Calc::abs(anim->scale.x) * facing;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Horizontal Movement
|
// NORMAL STATE
|
||||||
|
if (m_state == st_normal)
|
||||||
{
|
{
|
||||||
// Acceleration
|
// Current Animation
|
||||||
mover->speed.x += input * (m_on_ground ? ground_accel : air_accel) * Time::delta;
|
if (m_on_ground)
|
||||||
|
|
||||||
// Maxspeed
|
|
||||||
auto maxspd = (m_on_ground ? max_ground_speed : max_air_speed);
|
|
||||||
if (Calc::abs(mover->speed.x) > maxspd)
|
|
||||||
{
|
{
|
||||||
mover->speed.x = Calc::approach(
|
if (input != 0)
|
||||||
mover->speed.x,
|
anim->play("run");
|
||||||
Calc::sign(mover->speed.x) * maxspd,
|
else
|
||||||
2000 * Time::delta);
|
anim->play("idle");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
anim->play("jump");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Friction
|
// Horizontal Movement
|
||||||
if (input == 0 && m_on_ground)
|
{
|
||||||
mover->speed.x = Calc::approach(mover->speed.x, 0, friction * Time::delta);
|
// Acceleration
|
||||||
|
mover->speed.x += input * (m_on_ground ? ground_accel : air_accel) * Time::delta;
|
||||||
|
|
||||||
// Facing Direction
|
// Maxspeed
|
||||||
if (input != 0 && m_on_ground)
|
auto maxspd = (m_on_ground ? max_ground_speed : max_air_speed);
|
||||||
facing = input;
|
if (Calc::abs(mover->speed.x) > maxspd)
|
||||||
|
{
|
||||||
|
mover->speed.x = Calc::approach(
|
||||||
|
mover->speed.x,
|
||||||
|
Calc::sign(mover->speed.x) * maxspd,
|
||||||
|
2000 * Time::delta);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Friction
|
||||||
|
if (input == 0 && m_on_ground)
|
||||||
|
mover->speed.x = Calc::approach(mover->speed.x, 0, friction * Time::delta);
|
||||||
|
|
||||||
|
// Facing Direction
|
||||||
|
if (input != 0 && m_on_ground)
|
||||||
|
m_facing = input;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Invoke Jumping
|
||||||
|
{
|
||||||
|
if (input_jump.pressed() && mover->on_ground())
|
||||||
|
{
|
||||||
|
input_jump.clear_press_buffer();
|
||||||
|
anim->scale = Vec2(m_facing * 0.65f, 1.4f);
|
||||||
|
mover->speed.x = input * max_air_speed;
|
||||||
|
m_jump_timer = jump_time;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Begin Attack
|
||||||
|
if (input_attack.pressed())
|
||||||
|
{
|
||||||
|
input_attack.clear_press_buffer();
|
||||||
|
|
||||||
|
m_state = st_attack;
|
||||||
|
m_attack_timer = anim->sprite()->get_animation("attack")->duration();
|
||||||
|
|
||||||
|
if (m_on_ground)
|
||||||
|
mover->speed.x = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ATTACK STATE
|
||||||
|
else if (m_state == st_attack)
|
||||||
|
{
|
||||||
|
anim->play("attack");
|
||||||
|
m_attack_timer -= Time::delta;
|
||||||
|
|
||||||
|
if (m_attack_timer <= 0)
|
||||||
|
{
|
||||||
|
anim->play("idle");
|
||||||
|
m_state = st_normal;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Variable Jumping
|
||||||
|
if (m_jump_timer > 0)
|
||||||
|
{
|
||||||
|
mover->speed.y = -100;
|
||||||
|
m_jump_timer -= Time::delta;
|
||||||
|
if (!input_jump.down())
|
||||||
|
m_jump_timer = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Gravity
|
// Gravity
|
||||||
|
@ -86,22 +154,4 @@ void Player::update()
|
||||||
|
|
||||||
mover->speed.y += grav * Time::delta;
|
mover->speed.y += grav * Time::delta;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Jumping
|
|
||||||
{
|
|
||||||
if (input_jump.pressed() && mover->on_ground())
|
|
||||||
{
|
|
||||||
anim->scale = Vec2(facing * 0.65f, 1.4f);
|
|
||||||
mover->speed.x = input * max_air_speed;
|
|
||||||
m_jump_timer = jump_time;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (m_jump_timer > 0)
|
|
||||||
{
|
|
||||||
mover->speed.y = -100;
|
|
||||||
m_jump_timer -= Time::delta;
|
|
||||||
if (!input_jump.down())
|
|
||||||
m_jump_timer = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,15 +9,21 @@ namespace TL
|
||||||
class Player : public Component
|
class Player : public Component
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
static constexpr int st_normal = 0;
|
||||||
|
static constexpr int st_attack = 1;
|
||||||
|
|
||||||
VirtualStick input_move;
|
VirtualStick input_move;
|
||||||
VirtualButton input_jump;
|
VirtualButton input_jump;
|
||||||
|
VirtualButton input_attack;
|
||||||
|
|
||||||
Player();
|
Player();
|
||||||
void update() override;
|
void update() override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
int facing = 1;
|
int m_state = st_normal;
|
||||||
|
int m_facing = 1;
|
||||||
float m_jump_timer = 0;
|
float m_jump_timer = 0;
|
||||||
|
float m_attack_timer = 0;
|
||||||
bool m_on_ground;
|
bool m_on_ground;
|
||||||
};
|
};
|
||||||
}
|
}
|
|
@ -52,10 +52,12 @@ void Tilemap::set_cells(int x, int y, int w, int h, const Subtexture* tex)
|
||||||
|
|
||||||
void Tilemap::render(Batch& batch)
|
void Tilemap::render(Batch& batch)
|
||||||
{
|
{
|
||||||
|
batch.push_matrix(Mat3x2::create_translation(entity()->position));
|
||||||
for (int x = 0; x < m_columns; x ++)
|
for (int x = 0; x < m_columns; x ++)
|
||||||
for (int y = 0; y < m_rows; y ++)
|
for (int y = 0; y < m_rows; y ++)
|
||||||
if (m_grid[x + y * m_columns].texture)
|
if (m_grid[x + y * m_columns].texture)
|
||||||
{
|
{
|
||||||
batch.tex(m_grid[x + y * m_columns], Vec2(x * m_tile_width, y * m_tile_height));
|
batch.tex(m_grid[x + y * m_columns], Vec2(x * m_tile_width, y * m_tile_height));
|
||||||
}
|
}
|
||||||
|
batch.pop_matrix();
|
||||||
}
|
}
|
||||||
|
|
118
src/game.cpp
118
src/game.cpp
|
@ -4,10 +4,16 @@
|
||||||
#include "assets/tileset.h"
|
#include "assets/tileset.h"
|
||||||
#include "components/collider.h"
|
#include "components/collider.h"
|
||||||
#include "components/tilemap.h"
|
#include "components/tilemap.h"
|
||||||
|
#include "components/player.h"
|
||||||
#include "factory.h"
|
#include "factory.h"
|
||||||
|
|
||||||
using namespace TL;
|
using namespace TL;
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
constexpr float transition_duration = 0.75f;
|
||||||
|
}
|
||||||
|
|
||||||
void Game::startup()
|
void Game::startup()
|
||||||
{
|
{
|
||||||
// load our content
|
// load our content
|
||||||
|
@ -21,6 +27,8 @@ void Game::startup()
|
||||||
|
|
||||||
m_draw_colliders = false;
|
m_draw_colliders = false;
|
||||||
|
|
||||||
|
// camera setup
|
||||||
|
camera = Vec2::zero;
|
||||||
load_room(Point(0, 0));
|
load_room(Point(0, 0));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -30,14 +38,16 @@ void Game::load_room(Point cell)
|
||||||
BLAH_ASSERT(grid, "Room doesn't exist!");
|
BLAH_ASSERT(grid, "Room doesn't exist!");
|
||||||
room = cell;
|
room = cell;
|
||||||
|
|
||||||
// destroy all the entities
|
// get room offset
|
||||||
world.clear();
|
auto offset = Point(cell.x * width, cell.y * height);
|
||||||
|
|
||||||
// get the castle tileset for now
|
// get the castle tileset for now
|
||||||
auto castle = Content::find_tileset("castle");
|
auto castle = Content::find_tileset("castle");
|
||||||
|
auto grass = Content::find_tileset("grass");
|
||||||
|
auto plants = Content::find_tileset("plants");
|
||||||
|
|
||||||
// make the floor
|
// make the floor
|
||||||
auto floor = world.add_entity();
|
auto floor = world.add_entity(offset);
|
||||||
auto tilemap = floor->add(Tilemap(8, 8, columns, rows));
|
auto tilemap = floor->add(Tilemap(8, 8, columns, rows));
|
||||||
auto solids = floor->add(Collider::make_grid(8, 40, 23));
|
auto solids = floor->add(Collider::make_grid(8, 40, 23));
|
||||||
solids->mask = Mask::solid;
|
solids->mask = Mask::solid;
|
||||||
|
@ -46,6 +56,7 @@ void Game::load_room(Point cell)
|
||||||
for (int x = 0; x < columns; x ++)
|
for (int x = 0; x < columns; x ++)
|
||||||
for (int y = 0; y < rows; y++)
|
for (int y = 0; y < rows; y++)
|
||||||
{
|
{
|
||||||
|
Point world_position = offset + Point(x * tile_width, y * tile_height);
|
||||||
Color col = grid->pixels[x + y * columns];
|
Color col = grid->pixels[x + y * columns];
|
||||||
uint32_t rgb =
|
uint32_t rgb =
|
||||||
((uint32_t)col.r << 16) |
|
((uint32_t)col.r << 16) |
|
||||||
|
@ -58,15 +69,27 @@ void Game::load_room(Point cell)
|
||||||
case 0x000000:
|
case 0x000000:
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// solids
|
// castle tiles
|
||||||
case 0xffffff:
|
case 0xffffff:
|
||||||
tilemap->set_cell(x, y, &castle->random_tile());
|
tilemap->set_cell(x, y, &castle->random_tile());
|
||||||
solids->set_cell(x, y, true);
|
solids->set_cell(x, y, true);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// player
|
// grass tiles
|
||||||
|
case 0x8f974a:
|
||||||
|
tilemap->set_cell(x, y, &grass->random_tile());
|
||||||
|
solids->set_cell(x, y, true);
|
||||||
|
break;
|
||||||
|
|
||||||
|
// plants tiles
|
||||||
|
case 0x4b692f:
|
||||||
|
tilemap->set_cell(x, y, &plants->random_tile());
|
||||||
|
break;
|
||||||
|
|
||||||
|
// player (only if it doesn't already exist)
|
||||||
case 0x6abe30:
|
case 0x6abe30:
|
||||||
Factory::player(&world, Point(x * tile_width + tile_width / 2, (y + 1) * tile_height));
|
if (!world.first<Player>())
|
||||||
|
Factory::player(&world, world_position + Point(tile_width / 2, tile_height));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -79,12 +102,87 @@ void Game::shutdown()
|
||||||
|
|
||||||
void Game::update()
|
void Game::update()
|
||||||
{
|
{
|
||||||
|
// Toggle Collider Render
|
||||||
if (Input::pressed(Key::F1))
|
if (Input::pressed(Key::F1))
|
||||||
m_draw_colliders = !m_draw_colliders;
|
m_draw_colliders = !m_draw_colliders;
|
||||||
if (Input::pressed(Key::F2))
|
|
||||||
load_room(room);
|
|
||||||
|
|
||||||
world.update();
|
// Reload Current Room
|
||||||
|
if (Input::pressed(Key::F2))
|
||||||
|
{
|
||||||
|
m_transition = false;
|
||||||
|
world.clear();
|
||||||
|
load_room(room);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Normal Update
|
||||||
|
if (!m_transition)
|
||||||
|
{
|
||||||
|
world.update();
|
||||||
|
|
||||||
|
auto player = world.first<Player>();
|
||||||
|
if (player)
|
||||||
|
{
|
||||||
|
auto pos = player->entity()->position;
|
||||||
|
auto bounds = RectI(room.x * width, room.y * height, width, height);
|
||||||
|
if (!bounds.contains(pos))
|
||||||
|
{
|
||||||
|
// target room
|
||||||
|
Point next_room = Point(pos.x / width, pos.y / height);
|
||||||
|
if (pos.x < 0) next_room.x--;
|
||||||
|
if (pos.y < 0) next_room.y--;
|
||||||
|
|
||||||
|
// see if room exists
|
||||||
|
if (Content::find_room(next_room))
|
||||||
|
{
|
||||||
|
// transiton to it!
|
||||||
|
m_transition = true;
|
||||||
|
m_next_ease = 0;
|
||||||
|
m_next_room = next_room;
|
||||||
|
m_last_room = room;
|
||||||
|
|
||||||
|
// store entities from the previous room
|
||||||
|
m_last_entities.clear();
|
||||||
|
Entity* e = world.first_entity();
|
||||||
|
while (e)
|
||||||
|
{
|
||||||
|
m_last_entities.push_back(e);
|
||||||
|
e = e->next();
|
||||||
|
}
|
||||||
|
|
||||||
|
// load contents of the next room
|
||||||
|
load_room(next_room);
|
||||||
|
}
|
||||||
|
// doesn't exist, clamp player
|
||||||
|
else
|
||||||
|
{
|
||||||
|
player->entity()->position = Point(
|
||||||
|
Calc::clamp_int(pos.x, bounds.x, bounds.x + bounds.w),
|
||||||
|
Calc::clamp_int(pos.y, bounds.y, bounds.y + bounds.h));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Room Transition routine
|
||||||
|
else
|
||||||
|
{
|
||||||
|
auto last_cam = Vec2(m_last_room.x * width, m_last_room.y * height);
|
||||||
|
auto next_cam = Vec2(m_next_room.x * width, m_next_room.y * height);
|
||||||
|
|
||||||
|
m_next_ease = Calc::approach(m_next_ease, 1.0f, Time::delta / transition_duration);
|
||||||
|
camera = last_cam + (next_cam - last_cam) * Ease::cube_in_out(m_next_ease);
|
||||||
|
|
||||||
|
if (m_next_ease >= 1.0f)
|
||||||
|
{
|
||||||
|
// delete old objects (except player!)
|
||||||
|
for (auto& it : m_last_entities)
|
||||||
|
{
|
||||||
|
if (!it->get<Player>())
|
||||||
|
world.destroy_entity(it);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_transition = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Game::render()
|
void Game::render()
|
||||||
|
@ -93,6 +191,7 @@ void Game::render()
|
||||||
{
|
{
|
||||||
buffer->clear(0x150e22);
|
buffer->clear(0x150e22);
|
||||||
|
|
||||||
|
batch.push_matrix(Mat3x2::create_translation(-camera));
|
||||||
world.render(batch);
|
world.render(batch);
|
||||||
|
|
||||||
if (m_draw_colliders)
|
if (m_draw_colliders)
|
||||||
|
@ -105,6 +204,7 @@ void Game::render()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
batch.pop_matrix();
|
||||||
batch.render(buffer);
|
batch.render(buffer);
|
||||||
batch.clear();
|
batch.clear();
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,6 +20,7 @@ namespace TL
|
||||||
FrameBufferRef buffer;
|
FrameBufferRef buffer;
|
||||||
Batch batch;
|
Batch batch;
|
||||||
Point room;
|
Point room;
|
||||||
|
Vec2 camera;
|
||||||
|
|
||||||
void load_room(Point cell);
|
void load_room(Point cell);
|
||||||
void startup();
|
void startup();
|
||||||
|
@ -29,5 +30,10 @@ namespace TL
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool m_draw_colliders;
|
bool m_draw_colliders;
|
||||||
|
bool m_transition = false;
|
||||||
|
float m_next_ease;
|
||||||
|
Point m_next_room;
|
||||||
|
Point m_last_room;
|
||||||
|
Vector<Entity*> m_last_entities;
|
||||||
};
|
};
|
||||||
}
|
}
|
Loading…
Reference in New Issue
Block a user