From 5522ada351c842afe3f674445746ea2bbcfe3af5 Mon Sep 17 00:00:00 2001 From: Serin Delaunay Date: Thu, 7 May 2020 00:29:16 +0100 Subject: [PATCH 1/7] Basic version of TargetSprite. --- ppb/features/default_sprites.py | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 ppb/features/default_sprites.py diff --git a/ppb/features/default_sprites.py b/ppb/features/default_sprites.py new file mode 100644 index 00000000..be42930f --- /dev/null +++ b/ppb/features/default_sprites.py @@ -0,0 +1,11 @@ +import ppb + + +class TargetSprite(ppb.BaseSprite): + """Sprite that moves to a given target""" + target = ppb.Vector(0, 0) + speed = 1.0 + + def on_update(self, update_event, signal): + direction = (self.position - self.target).normalize() + self.position += direction * self.speed * update_event.time_delta From 280e0f2a6d0cb2aa90a78a288b3e28489b3eb329 Mon Sep 17 00:00:00 2001 From: Serin Delaunay Date: Thu, 7 May 2020 00:43:37 +0100 Subject: [PATCH 2/7] Avoid zero division in TargetSprite. --- ppb/features/default_sprites.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/ppb/features/default_sprites.py b/ppb/features/default_sprites.py index be42930f..a5137706 100644 --- a/ppb/features/default_sprites.py +++ b/ppb/features/default_sprites.py @@ -7,5 +7,10 @@ class TargetSprite(ppb.BaseSprite): speed = 1.0 def on_update(self, update_event, signal): - direction = (self.position - self.target).normalize() - self.position += direction * self.speed * update_event.time_delta + offset = self.position - self.target + distance_this_tick = self.speed * update_event.time_delta + if offset.length <= distance_this_tick: + self.position = self.target + else: + direction = offset.normalize() + self.position += direction * self.speed * update_event.time_delta From 9a00bbe480626b1485daa1f2badd1fe8c56bfb6e Mon Sep 17 00:00:00 2001 From: Serin Delaunay Date: Thu, 7 May 2020 00:51:02 +0100 Subject: [PATCH 3/7] Add Serin Delaunay to CONTRIBUTORS.md --- CONTRIBUTORS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index 9ca025b6..71805528 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -31,3 +31,4 @@ Halle Jones|HJones@aliacy.com|| [Arul Prabakaran](https://github.com/arulprabakaran) | [arul.prabakaran@gmail.com](arul.prabakaran@gmail.com) | [arulpraba](https://twitter.com/arulpraba) [Calvin Spealman](https://github.com/ironfroggy) | [ironfroggy@gmail.com](ironfroggy@gmail.com) | [@ironfroggy](https://twitter.com/ironfroggy) [Sanket Dasgupta](https://github.com/SanketDG) | [sanketdasgupta@gmail.com](sanketdasgupta@gmail.com) | [@SanketDG](https://twitter.com/SanketDG) +[Serin Delaunay](https://github.com/serin-delaunay) | | [SerinDelaunay](https://twitter.com/SerinDelaunay) From ff71d3e1e69718f3fde1b5efc583a81faaaf03ac Mon Sep 17 00:00:00 2001 From: Serin Delaunay Date: Thu, 7 May 2020 01:43:42 +0100 Subject: [PATCH 4/7] Test TargetSprite and correct movement polarity. Alter subclass. --- ppb/features/default_sprites.py | 4 ++-- tests/test_default_sprites.py | 13 +++++++++++++ 2 files changed, 15 insertions(+), 2 deletions(-) create mode 100644 tests/test_default_sprites.py diff --git a/ppb/features/default_sprites.py b/ppb/features/default_sprites.py index a5137706..7bca12a8 100644 --- a/ppb/features/default_sprites.py +++ b/ppb/features/default_sprites.py @@ -1,13 +1,13 @@ import ppb -class TargetSprite(ppb.BaseSprite): +class TargetSprite(ppb.sprites.BaseSprite): """Sprite that moves to a given target""" target = ppb.Vector(0, 0) speed = 1.0 def on_update(self, update_event, signal): - offset = self.position - self.target + offset = self.target - self.position distance_this_tick = self.speed * update_event.time_delta if offset.length <= distance_this_tick: self.position = self.target diff --git a/tests/test_default_sprites.py b/tests/test_default_sprites.py new file mode 100644 index 00000000..7309b404 --- /dev/null +++ b/tests/test_default_sprites.py @@ -0,0 +1,13 @@ +import unittest +import ppb +from ppb import Vector +from ppb.features.default_sprites import TargetSprite + + +def test_rotatable_base_sprite(): + target_sprite = TargetSprite() + target_sprite.target = Vector(3, 4) + target_sprite.speed = 5.0 + target_sprite.on_update(ppb.events.Update(0.2), lambda x: None) + + assert target_sprite.position.isclose((0.6, 0.8)) From 921198b3b503d5eb8ba598f6487c0c5a95c86884 Mon Sep 17 00:00:00 2001 From: Serin Delaunay Date: Thu, 7 May 2020 02:40:11 +0100 Subject: [PATCH 5/7] Add exponential movement to TargetSprite with min/max speed and test. Correct name of linear speed test. --- ppb/features/default_sprites.py | 27 +++++++++++++++++++++++---- tests/test_default_sprites.py | 33 ++++++++++++++++++++++++++++++++- 2 files changed, 55 insertions(+), 5 deletions(-) diff --git a/ppb/features/default_sprites.py b/ppb/features/default_sprites.py index 7bca12a8..f69ddbcd 100644 --- a/ppb/features/default_sprites.py +++ b/ppb/features/default_sprites.py @@ -1,16 +1,35 @@ import ppb +import math class TargetSprite(ppb.sprites.BaseSprite): - """Sprite that moves to a given target""" + """Sprite that moves to a given target.""" target = ppb.Vector(0, 0) speed = 1.0 + exponential_speed = 0.0 + max_speed = math.inf + min_speed = -math.inf def on_update(self, update_event, signal): + if self.max_speed < self.min_speed: + raise ValueError("TargetSprite maximum speed cannot be less than minimum speed.") offset = self.target - self.position - distance_this_tick = self.speed * update_event.time_delta - if offset.length <= distance_this_tick: + distance_to_target = offset.length + max_distance = self.max_speed * update_event.time_delta + min_distance = self.min_speed * update_event.time_delta + linear_distance = self.speed * update_event.time_delta + exponential_distance = distance_to_target * self._exponential_decay(update_event.time_delta) + total_distance = linear_distance + exponential_distance + total_distance = min(total_distance, max_distance) + total_distance = max(total_distance, min_distance) + if distance_to_target <= total_distance: self.position = self.target else: direction = offset.normalize() - self.position += direction * self.speed * update_event.time_delta + self.position += direction * total_distance + + def _exponential_decay(self, time_delta): + decay_rate = 1. - self.exponential_speed + remaining = decay_rate ** time_delta + decay_amount = 1. - remaining + return decay_amount diff --git a/tests/test_default_sprites.py b/tests/test_default_sprites.py index 7309b404..e53bb146 100644 --- a/tests/test_default_sprites.py +++ b/tests/test_default_sprites.py @@ -4,10 +4,41 @@ from ppb.features.default_sprites import TargetSprite -def test_rotatable_base_sprite(): +def test_target_sprite_linear(): target_sprite = TargetSprite() target_sprite.target = Vector(3, 4) target_sprite.speed = 5.0 target_sprite.on_update(ppb.events.Update(0.2), lambda x: None) assert target_sprite.position.isclose((0.6, 0.8)) + + +def test_target_sprite_exponential(): + target_sprite = TargetSprite() + target_sprite.target = Vector(3, -4) + target_sprite.speed = 0.0 + target_sprite.exponential_speed = 0.5 + target_sprite.on_update(ppb.events.Update(2.0), lambda x: None) + + assert target_sprite.position.isclose((2.25, -3.0)) + + +def test_target_sprite_max_speed(): + target_sprite = TargetSprite() + target_sprite.target = Vector(-3, 4) + target_sprite.speed = 500. + target_sprite.exponential_speed = 0.99 + target_sprite.max_speed = 1.0 + target_sprite.on_update(ppb.events.Update(2.0), lambda x: None) + + assert target_sprite.position.isclose((-1.2, 1.6)) + + +def test_target_sprite_min_speed(): + target_sprite = TargetSprite() + target_sprite.target = Vector(-3, -4) + target_sprite.speed = 0.0 + target_sprite.min_speed = 2.0 + target_sprite.on_update(ppb.events.Update(1.0), lambda x: None) + + assert target_sprite.position.isclose((-1.2, -1.6)) From 24da6f84dd3af216deb61711b4120e5d27be7702 Mon Sep 17 00:00:00 2001 From: Serin Delaunay Date: Thu, 7 May 2020 02:56:48 +0100 Subject: [PATCH 6/7] Docstring TargetSprite parameters. --- ppb/features/default_sprites.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/ppb/features/default_sprites.py b/ppb/features/default_sprites.py index f69ddbcd..5a91a1c5 100644 --- a/ppb/features/default_sprites.py +++ b/ppb/features/default_sprites.py @@ -3,7 +3,15 @@ class TargetSprite(ppb.sprites.BaseSprite): - """Sprite that moves to a given target.""" + """Sprite that moves to a given target. + + :param target: Vector that the sprite moves towards. + :param speed: Distance per second that the sprite travels with linear motion. + :param exponential_speed: Fraction of the distance to the target that the sprite travels + per second with exponential motion. Should normally be in the range [0.0, 1.0]. + :param max_speed: Maximum distance per second that the sprite can travel. + :param min_speed: Minimum distance per second that the sprite travels when not in range of the target. + """ target = ppb.Vector(0, 0) speed = 1.0 exponential_speed = 0.0 From 9a30bced58b8933475789ec3fb0222134e2541b2 Mon Sep 17 00:00:00 2001 From: Serin Delaunay Date: Thu, 7 May 2020 03:14:45 +0100 Subject: [PATCH 7/7] Update ppb/features/default_sprites.py Co-authored-by: Piper Thunstrom --- ppb/features/default_sprites.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ppb/features/default_sprites.py b/ppb/features/default_sprites.py index 5a91a1c5..7beae756 100644 --- a/ppb/features/default_sprites.py +++ b/ppb/features/default_sprites.py @@ -2,7 +2,7 @@ import math -class TargetSprite(ppb.sprites.BaseSprite): +class TargetSprite(ppb.Sprite): """Sprite that moves to a given target. :param target: Vector that the sprite moves towards.