Skip to content

Commit 4231287

Browse files
committed
Smooth badguy and other object motion when frame prediction is on
This commit extrapolates the drawn position for badguys and some other moving objects, making their motion look smoother when both the frame rate is high and frame prediction is on. This is already being done for Tux, but that is not enough to always produce the illusion of smooth motion. Without this commit one can at high frame rates see choppy motion on carried objects when Tux is running, and on quickly moving badguys, like MrIceBlock.
1 parent c39b073 commit 4231287

11 files changed

+42
-16
lines changed

src/badguy/badguy.cpp

+12-7
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
#include "object/water_drop.hpp"
3232
#include "sprite/sprite.hpp"
3333
#include "sprite/sprite_manager.hpp"
34+
#include "supertux/constants.hpp"
3435
#include "supertux/level.hpp"
3536
#include "supertux/sector.hpp"
3637
#include "supertux/tile.hpp"
@@ -141,10 +142,13 @@ BadGuy::draw(DrawingContext& context)
141142
{
142143
if (!m_sprite.get()) return;
143144

145+
Vector draw_offset = context.get_time_offset() * m_physic.get_velocity();
146+
Vector draw_pos = get_pos() + draw_offset;
147+
144148
if (m_state == STATE_INIT || m_state == STATE_INACTIVE)
145149
{
146150
if (Editor::is_active()) {
147-
m_sprite->draw(context.color(), get_pos(), m_layer, m_flip);
151+
m_sprite->draw(context.color(), draw_pos, m_layer, m_flip);
148152
}
149153
}
150154
else
@@ -153,27 +157,27 @@ BadGuy::draw(DrawingContext& context)
153157
{
154158
context.push_transform();
155159
context.set_flip(context.get_flip() ^ VERTICAL_FLIP);
156-
m_sprite->draw(context.color(), get_pos(), m_layer, m_flip);
160+
m_sprite->draw(context.color(), draw_pos, m_layer, m_flip);
157161
context.pop_transform();
158162
}
159163
else
160164
{
161165
if (m_unfreeze_timer.started() && m_unfreeze_timer.get_timeleft() <= 1.f)
162166
{
163-
m_sprite->draw(context.color(), get_pos() + Vector(graphicsRandom.randf(-3, 3), 0.f), m_layer-1, m_flip);
167+
m_sprite->draw(context.color(), draw_pos + Vector(graphicsRandom.randf(-3, 3), 0.f), m_layer - 1, m_flip);
164168
if (is_portable())
165-
m_freezesprite->draw(context.color(), get_pos() + Vector(graphicsRandom.randf(-3, 3), 0.f), m_layer);
169+
m_freezesprite->draw(context.color(), draw_pos + Vector(graphicsRandom.randf(-3, 3), 0.f), m_layer);
166170
}
167171
else
168172
{
169173
if (m_frozen && is_portable())
170-
m_freezesprite->draw(context.color(), get_pos(), m_layer);
171-
m_sprite->draw(context.color(), get_pos(), m_layer - (m_frozen ? 1 : 0), m_flip);
174+
m_freezesprite->draw(context.color(), draw_pos, m_layer);
175+
m_sprite->draw(context.color(), draw_pos, m_layer - (m_frozen ? 1 : 0), m_flip);
172176
}
173177

174178
if (m_glowing)
175179
{
176-
m_lightsprite->draw(context.light(), m_col.m_bbox.get_middle(), 0);
180+
m_lightsprite->draw(context.light(), m_col.m_bbox.get_middle() + draw_offset, 0);
177181
}
178182
}
179183
}
@@ -892,6 +896,7 @@ BadGuy::grab(MovingObject& object, const Vector& pos, Direction dir_)
892896
{
893897
Portable::grab(object, pos, dir_);
894898
m_col.set_movement(pos - get_pos());
899+
m_physic.set_velocity(m_col.get_movement() * LOGICAL_FPS);
895900
m_dir = dir_;
896901
if (m_frozen)
897902
{

src/badguy/crusher.cpp

+5-4
Original file line numberDiff line numberDiff line change
@@ -468,14 +468,15 @@ Crusher::spawn_roots(Direction direction)
468468
void
469469
Crusher::draw(DrawingContext& context)
470470
{
471-
m_sprite->draw(context.color(), get_pos(), m_layer + 2, m_flip);
471+
Vector draw_pos = get_pos() + m_physic.get_velocity() * context.get_time_offset();
472+
m_sprite->draw(context.color(), draw_pos, m_layer + 2, m_flip);
472473
if (m_sprite->has_action("whites"))
473474
{
474475
// Draw crusher's eyes slightly behind.
475-
m_lefteye->draw(context.color(), get_pos() + eye_position(false), m_layer + 1, m_flip);
476-
m_righteye->draw(context.color(), get_pos() + eye_position(true), m_layer + 1, m_flip);
476+
m_lefteye->draw(context.color(), draw_pos + eye_position(false), m_layer + 1, m_flip);
477+
m_righteye->draw(context.color(), draw_pos + eye_position(true), m_layer + 1, m_flip);
477478
// Draw the whites of crusher's eyes even further behind.
478-
m_whites->draw(context.color(), get_pos(), m_layer, m_flip);
479+
m_whites->draw(context.color(), draw_pos, m_layer, m_flip);
479480
}
480481
}
481482

src/badguy/mrbomb.cpp

+2
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
#include "object/portable.hpp"
2727
#include "sprite/sprite.hpp"
2828
#include "sprite/sprite_manager.hpp"
29+
#include "supertux/constants.hpp"
2930
#include "supertux/sector.hpp"
3031
#include "util/reader_mapping.hpp"
3132

@@ -242,6 +243,7 @@ MrBomb::grab(MovingObject& object, const Vector& pos, Direction dir_)
242243
set_action(dir_);
243244

244245
m_col.set_movement(pos - get_pos());
246+
m_physic.set_velocity(m_col.get_movement() * LOGICAL_FPS);
245247
m_dir = dir_;
246248
set_colgroup_active(COLGROUP_DISABLED);
247249
}

src/badguy/mriceblock.cpp

+2
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
#include "object/player.hpp"
2525
#include "object/portable.hpp"
2626
#include "sprite/sprite.hpp"
27+
#include "supertux/constants.hpp"
2728
#include "supertux/sector.hpp"
2829

2930
namespace {
@@ -356,6 +357,7 @@ MrIceBlock::grab(MovingObject& object, const Vector& pos, Direction dir_)
356357

357358
Portable::grab(object, pos, dir_);
358359
m_col.set_movement(pos - get_pos());
360+
m_physic.set_velocity(m_col.get_movement() * LOGICAL_FPS);
359361
m_dir = dir_;
360362
set_action("flat", m_dir, /* loops = */ -1);
361363
set_state(ICESTATE_GRABBED);

src/badguy/rcrystallo.cpp

+2-1
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,8 @@ void
148148
RCrystallo::draw(DrawingContext& context)
149149
{
150150
context.push_transform();
151-
m_sprite->draw(context.color(), get_pos(), m_layer);
151+
Vector draw_pos = get_pos() + m_physic.get_velocity() * context.get_time_offset();
152+
m_sprite->draw(context.color(), draw_pos, m_layer);
152153
context.pop_transform();
153154
}
154155

src/badguy/snail.cpp

+2
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
#include "object/player.hpp"
2424
#include "object/portable.hpp"
2525
#include "sprite/sprite.hpp"
26+
#include "supertux/constants.hpp"
2627
#include "supertux/sector.hpp"
2728

2829
namespace {
@@ -390,6 +391,7 @@ Snail::grab(MovingObject& object, const Vector& pos, Direction dir_)
390391
if (m_frozen)
391392
BadGuy::grab(object, pos, dir_);
392393
m_col.set_movement(pos - get_pos());
394+
m_physic.set_velocity(m_col.get_movement() * LOGICAL_FPS);
393395
m_dir = dir_;
394396
if (!m_frozen)
395397
{

src/object/player.cpp

+1
Original file line numberDiff line numberDiff line change
@@ -2017,6 +2017,7 @@ Player::draw(DrawingContext& context)
20172017
// if Tux is above camera, draw little "air arrow" to show where he is x-wise
20182018
if (m_col.m_bbox.get_bottom() - 16 < Sector::get().get_camera().get_translation().y) {
20192019
float px = m_col.m_bbox.get_left() + (m_col.m_bbox.get_right() - m_col.m_bbox.get_left() - static_cast<float>(m_airarrow.get()->get_width())) / 2.0f;
2020+
px += context.get_time_offset() * m_physic.get_velocity().x;
20202021
float py = Sector::get().get_camera().get_translation().y;
20212022
py += std::min(((py - (m_col.m_bbox.get_bottom() + 16)) / 4), 16.0f);
20222023
context.color().draw_surface(m_airarrow, Vector(px, py), LAYER_HUD - 1);

src/object/rock.cpp

+9
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
#include "object/lit_object.hpp"
2525
#include "object/pushbutton.hpp"
2626
#include "object/trampoline.hpp"
27+
#include "supertux/constants.hpp"
2728
#include "supertux/sector.hpp"
2829
#include "supertux/tile.hpp"
2930
#include "object/player.hpp"
@@ -229,6 +230,7 @@ Rock::grab(MovingObject& object, const Vector& pos, Direction dir_)
229230
Portable::grab(object, pos, dir_);
230231
Vector movement = pos - get_pos();
231232
m_col.set_movement(movement);
233+
physic.set_velocity(movement * LOGICAL_FPS);
232234
last_movement = movement;
233235
set_group(COLGROUP_TOUCHABLE); //needed for lanterns catching willowisps
234236
on_ground = false;
@@ -272,6 +274,13 @@ Rock::ungrab(MovingObject& object, Direction dir)
272274
Portable::ungrab(object, dir);
273275
}
274276

277+
void
278+
Rock::draw(DrawingContext& context)
279+
{
280+
Vector offset = physic.get_velocity() * context.get_time_offset();
281+
m_sprite->draw(context.color(), get_pos() + offset, m_layer, m_flip);
282+
}
283+
275284
ObjectSettings
276285
Rock::get_settings()
277286
{

src/object/rock.hpp

+1
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ class Rock : public MovingSprite,
4444
virtual ObjectSettings get_settings() override;
4545
virtual GameObjectTypes get_types() const override;
4646
std::string get_default_sprite_name() const override;
47+
void draw(DrawingContext& context) override;
4748

4849
/** Adds velocity from wind */
4950
virtual void add_wind_velocity(const Vector& velocity, const Vector& end_speed);

src/object/smoke_cloud.cpp

+2-1
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,8 @@ SmokeCloud::update(float dt_sec)
3939
void
4040
SmokeCloud::draw(DrawingContext& context)
4141
{
42-
sprite->draw(context.color(), position, LAYER_OBJECTS + 1);
42+
Vector draw_pos = position - Vector(0.0, 120.0) * std::min(context.get_time_offset(), timer.get_timeleft());
43+
sprite->draw(context.color(), draw_pos, LAYER_OBJECTS + 1);
4344
}
4445

4546
/* EOF */

src/object/sprite_particle.cpp

+4-3
Original file line numberDiff line numberDiff line change
@@ -99,13 +99,14 @@ SpriteParticle::update(float dt_sec)
9999
void
100100
SpriteParticle::draw(DrawingContext& context)
101101
{
102-
sprite->draw(context.color(), position, drawing_layer);
102+
Vector draw_pos = position + velocity * context.get_time_offset();
103+
sprite->draw(context.color(), draw_pos, drawing_layer);
103104

104105
//Sparkles glow in the dark
105106
if (glow)
106107
{
107-
sprite->draw(context.light(), position, drawing_layer);
108-
lightsprite->draw(context.light(), position + Vector(12, 12), 0);
108+
sprite->draw(context.light(), draw_pos, drawing_layer);
109+
lightsprite->draw(context.light(), draw_pos + Vector(12, 12), 0);
109110
}
110111

111112
}

0 commit comments

Comments
 (0)