From c7d42a462efec2599a50cd47210367f8edc839d8 Mon Sep 17 00:00:00 2001 From: Sean Hagen Date: Wed, 16 Jun 2021 22:08:28 -0500 Subject: [PATCH 1/8] Created hitbox object. Working on unit testing --- game/common/enums.py | 1 + game/common/hitbox.py | 86 +++++++++++++++++++ game/common/items/item.py | 4 +- game/common/items/upgrade.py | 4 +- game/common/map_object.py | 4 +- .../common/moving/damaging/damaging_object.py | 4 +- game/common/moving/damaging/grenade.py | 4 +- game/common/moving/moving_object.py | 4 +- game/common/moving/shooter.py | 3 +- game/common/stats.py | 5 ++ game/common/wall.py | 4 +- game/common/walls.py | 17 ---- game/test_suite/tests/__init__.py | 4 +- .../tests/objects/test_damaging_object.py | 8 +- game/test_suite/tests/objects/test_grenade.py | 6 +- .../tests/objects/test_hitbox_object.py | 41 +++++++++ .../tests/objects/test_initialization.py | 7 +- .../tests/objects/test_moving_object.py | 6 +- game/test_suite/tests/objects/test_shooter.py | 2 +- 19 files changed, 165 insertions(+), 49 deletions(-) create mode 100644 game/common/hitbox.py delete mode 100644 game/common/walls.py create mode 100644 game/test_suite/tests/objects/test_hitbox_object.py diff --git a/game/common/enums.py b/game/common/enums.py index 95641cf2..eef19296 100644 --- a/game/common/enums.py +++ b/game/common/enums.py @@ -18,6 +18,7 @@ class ObjectType: item = 9 gun = 10 wall = 11 + hitbox = 12 class Upgrades: diff --git a/game/common/hitbox.py b/game/common/hitbox.py new file mode 100644 index 00000000..7e2ac99f --- /dev/null +++ b/game/common/hitbox.py @@ -0,0 +1,86 @@ +from game.common.game_object import GameObject +from game.common.stats import GameStats +from game.common.enums import * + + +class Hitbox(GameObject): + def __init__(self, width, height, xy_tuple): + super().__init__() + self.object_type = ObjectType.hitbox + self.width = width + self.height = height + # (x,y) tuple, where [0] is the x position and y is [1], which the hitbox is centered around + self.position = xy_tuple + + @property + def width(self): + return self.__width + + @property + def height(self): + return self.__height + + @property + def position(self): + return self.__position + + @property + def topLeft(self): + return (self.x - (self.width/2), self.y + (self.height/2)) + + @property + def topRight(self): + return (self.x + (self.width/2), self.y + (self.height/2)) + + @property + def bottomLeft(self): + return (self.x - (self.width/2), self.y - (self.height/2)) + + @property + def bottomRight(self): + return (self.x + (self.width/2), self.y - (self.height/2)) + + # set height between 0 and max + @height.setter + def height(self, val): + if 0 <= val <= GameStats.max_hitbox['height']: + self.__height = val + else: + raise Exception("Tried to set an invalid height for hitbox") + + # Set width for hitbox between 0 and max + @width.setter + def width(self, val): + if val >= 0 and val <= GameStats.max_hitbox['width']: + self.__width = val + else: + raise Exception("Tried to set an invalid width for hitbox") + + # set x between 0 and max game board width + @position.setter + def position(self, val): + if (0 <= val[0] <= GameStats.game_board_width) and (0 <= val[1] <= GameStats.game_board_height): + self.__position = val + else: + raise Exception("Tried to set an invalid xy position tuple for hitbox") + + def to_json(self): + data = super().to_json() + data['width'] = self.width + data['height'] = self.height + data['position'] = self.position + + return data + + def from_json(self, data): + super().from_json(data) + self.width = data['width'] + self.height = data['height'] + self.position = data['position'] + + def __str__(self): + return f""" + Height: {self.height} + Width: {self.width} + X: {self.position} + """ diff --git a/game/common/items/item.py b/game/common/items/item.py index 23e9ae8c..4f99ddc3 100644 --- a/game/common/items/item.py +++ b/game/common/items/item.py @@ -2,8 +2,8 @@ from game.common.enums import ObjectType class Item(MapObject): - def __init__(self, coordinates, hitbox, health=None, count = 1): - super().__init__(health, coordinates, hitbox, True) + def __init__(self, hitbox, health=None, count = 1): + super().__init__(health, hitbox, True) self.object_type = ObjectType.item self.count = count diff --git a/game/common/items/upgrade.py b/game/common/items/upgrade.py index d4da071d..00ee713b 100644 --- a/game/common/items/upgrade.py +++ b/game/common/items/upgrade.py @@ -5,8 +5,8 @@ class Upgrade(Item): - def __init__ (self, coordinates, hitbox, health, count, upgrade_enum = None, movement_enum = None, sight_enum = None ): - super().__init__(coordinates, hitbox, health, count) + def __init__ (self, hitbox, health, count, upgrade_enum = None, movement_enum = None, sight_enum = None ): + super().__init__(hitbox, health, count) self.upgrade_enum = upgrade_enum self.movement_enum = movement_enum self.sight_enum = sight_enum diff --git a/game/common/map_object.py b/game/common/map_object.py index 3fb75b59..2ce4caeb 100644 --- a/game/common/map_object.py +++ b/game/common/map_object.py @@ -3,12 +3,10 @@ class MapObject(GameObject): - def __init__(self, health=None, coordinates=None, hitbox=None, collidable=None): + def __init__(self, health=None, hitbox=None, collidable=None): super().__init__() self.object_type = ObjectType.map_object - self.health = health - self.coordinates = coordinates self.hitbox = hitbox self.collidable = collidable diff --git a/game/common/moving/damaging/damaging_object.py b/game/common/moving/damaging/damaging_object.py index 00833f04..cb784d6a 100644 --- a/game/common/moving/damaging/damaging_object.py +++ b/game/common/moving/damaging/damaging_object.py @@ -7,8 +7,8 @@ class DamagingObject(MovingObject): def __init__(self, range=0, damage=0, heading = None, speed = None, - health=None, coordinates=None, hitbox=None, collidable=None): - super().__init__(heading, speed, health, coordinates, hitbox, collidable) + health=None, hitbox=None, collidable=None): + super().__init__(heading, speed, health, hitbox, collidable) self.range = range self.damage = damage self.object_type = ObjectType.damaging_object diff --git a/game/common/moving/damaging/grenade.py b/game/common/moving/damaging/grenade.py index fc43f492..e792ed2d 100644 --- a/game/common/moving/damaging/grenade.py +++ b/game/common/moving/damaging/grenade.py @@ -5,8 +5,8 @@ class Grenade(DamagingObject): def __init__(self, fuse_time = GameStats.grenade_stats['min_fuse_time'], range= None, damage= None, - heading = None, speed = None, health=None, coordinates=None, hitbox=None, collidable=None): - super().__init__(range, damage, heading, speed, health, coordinates, hitbox, collidable) + heading = None, speed = None, health=None, hitbox=None, collidable=None): + super().__init__(range, damage, heading, speed, health, hitbox, collidable) self.fuse_time = fuse_time self.object_type = ObjectType.grenade diff --git a/game/common/moving/moving_object.py b/game/common/moving/moving_object.py index 9633ef7d..0603a43d 100644 --- a/game/common/moving/moving_object.py +++ b/game/common/moving/moving_object.py @@ -6,8 +6,8 @@ # Inherits MapObject class MovingObject(MapObject): - def __init__(self, heading=0, speed=0, health=None, coordinates=None, hitbox=None, collidable=None): - super().__init__(health, coordinates, hitbox, collidable) + def __init__(self, heading=0, speed=0, health=None, hitbox=None, collidable=None): + super().__init__(health, hitbox, collidable) self.heading = heading self.speed = speed self.object_type = ObjectType.moving_object diff --git a/game/common/moving/shooter.py b/game/common/moving/shooter.py index 9f560ee1..03e014ea 100644 --- a/game/common/moving/shooter.py +++ b/game/common/moving/shooter.py @@ -7,12 +7,11 @@ class Shooter(MovingObject): - def __init__(self, heading=0, speed=0, coordinates=GameStats.player_stats['starting_coordinates'][0]): + def __init__(self, heading=0, speed=0): super().__init__( heading, speed, GameStats.player_stats['starting_health'], - coordinates, GameStats.player_stats['hitbox'], collidable=True ) diff --git a/game/common/stats.py b/game/common/stats.py index 032f0867..b55768d4 100644 --- a/game/common/stats.py +++ b/game/common/stats.py @@ -50,6 +50,11 @@ class GameStats: 'max_damage': 100 } + max_hitbox = { + 'width': 100, + 'height': 100, + } + # Placeholder stats, stats may be created for all gun levels gun_stats = { GunType.none: {'pattern': ShotPattern.none, 'damage': 0, diff --git a/game/common/wall.py b/game/common/wall.py index aac1c977..98573ba9 100644 --- a/game/common/wall.py +++ b/game/common/wall.py @@ -4,8 +4,8 @@ class Wall(MapObject): - def __init__(self, coordinates, hitbox, health = GameStats.default_wall_health, destructible = False): - super().__init__(health, coordinates, hitbox, collidable = True ) + def __init__(self, hitbox, health = GameStats.default_wall_health, destructible = False): + super().__init__(health, hitbox, collidable = True ) self.destructible = destructible self.object_type = ObjectType.wall diff --git a/game/common/walls.py b/game/common/walls.py deleted file mode 100644 index 5c022256..00000000 --- a/game/common/walls.py +++ /dev/null @@ -1,17 +0,0 @@ -from game.common.map_object import MapObject -from game.common.stats import GameStats - - -class Wall(MapObject): - def __init__(self, coordinates, hitbox, health = GameStats.default_wall_health, destructible = False, collidable = True): - super().__init__(health, coordinates, hitbox, collidable ) - self.destructible = destructible - - def to_json(self): - data = super().to_json() - data['destructible'] = self.destructible - return data - - def from_json(self, data): - super().from_json(data) - self.destructible = data['destructible'] \ No newline at end of file diff --git a/game/test_suite/tests/__init__.py b/game/test_suite/tests/__init__.py index 4cfe261f..35683e52 100644 --- a/game/test_suite/tests/__init__.py +++ b/game/test_suite/tests/__init__.py @@ -7,6 +7,7 @@ from game.test_suite.tests.objects.test_grenade import TestGrenade from game.test_suite.tests.objects.test_initialization import TestInit from game.test_suite.tests.objects.test_shooter import TestShooterObject +from game.test_suite.tests.objects.test_hitbox_object import TestHitboxObject __all__ = [ 'TestGameBoard', @@ -14,5 +15,6 @@ 'TestDamagingObject', 'TestShooterObject', 'TestGrenade', - 'TestInit' + 'TestInit', + 'TestHitboxObject' ] \ No newline at end of file diff --git a/game/test_suite/tests/objects/test_damaging_object.py b/game/test_suite/tests/objects/test_damaging_object.py index 27a6ece3..9bfd8d80 100644 --- a/game/test_suite/tests/objects/test_damaging_object.py +++ b/game/test_suite/tests/objects/test_damaging_object.py @@ -2,11 +2,12 @@ from game.common.moving.damaging import damaging_object from game.common.moving.damaging.damaging_object import DamagingObject from game.common.stats import GameStats +from game.common.hitbox import Hitbox class TestDamagingObject(unittest.TestCase): def setUp(self): - self.dmgObj = DamagingObject(10, 10, heading= 1, speed= 1) + self.dmgObj = DamagingObject(10, 10, heading= 1, speed= 1, hitbox= Hitbox(5,5 ,(5,5)), collidable=True) def test_set_get_range_valid(self): self.dmgObj.range = 50 @@ -43,13 +44,12 @@ def test_set_get_damage_boundary_high(self): self.assertEqual(self.dmgObj.damage, GameStats.damaging_object_stats['max_damage']) def test_damaging_obj_parent_params(self): - testDmg = DamagingObject(range = 10, damage = 10, heading = 1, speed = 10, health = 1, coordinates=[{'x': 450, 'y': 450}, {'x': 50, 'y': 50}], - hitbox={'width': 10, 'height': 10}, collidable=True) + testDmg = DamagingObject(range = 10, damage = 10, heading = 1, speed = 10, health = 1, hitbox= Hitbox(5,5,(5,5)), collidable=True) self.assertIsNotNone(testDmg.heading) self.assertIsNotNone(testDmg.speed) - self.assertIsNotNone(testDmg.coordinates) + self.assertIsNotNone(testDmg.hitbox.position) self.assertIsNotNone(testDmg.hitbox) self.assertIsNotNone(testDmg.collidable) diff --git a/game/test_suite/tests/objects/test_grenade.py b/game/test_suite/tests/objects/test_grenade.py index b2d8d5cf..7af746df 100644 --- a/game/test_suite/tests/objects/test_grenade.py +++ b/game/test_suite/tests/objects/test_grenade.py @@ -1,3 +1,4 @@ +from game.common.hitbox import Hitbox import unittest from game.common.moving.damaging import grenade from game.common.moving.damaging.grenade import Grenade @@ -31,14 +32,13 @@ def test_set_get_fuse_time_boundary_high(self): def test_grenade_obj_parent_params(self): - testGrn = Grenade(fuse_time = 20, range = 10, damage = 10, heading = 1, speed = 10, health = 1, coordinates=[{'x': 450, 'y': 450}, {'x': 50, 'y': 50}], - hitbox={'width': 10, 'height': 10}, collidable=True) + testGrn = Grenade(fuse_time = 20, range = 10, damage = 10, heading = 1, speed = 10, health = 1, hitbox=Hitbox(10,10,(10,10)), collidable=True) self.assertIsNotNone(testGrn.range) self.assertIsNotNone(testGrn.damage) self.assertIsNotNone(testGrn.heading) self.assertIsNotNone(testGrn.speed) - self.assertIsNotNone(testGrn.coordinates) + self.assertIsNotNone(testGrn.hitbox.position) self.assertIsNotNone(testGrn.hitbox) self.assertIsNotNone(testGrn.collidable) diff --git a/game/test_suite/tests/objects/test_hitbox_object.py b/game/test_suite/tests/objects/test_hitbox_object.py new file mode 100644 index 00000000..a9700af4 --- /dev/null +++ b/game/test_suite/tests/objects/test_hitbox_object.py @@ -0,0 +1,41 @@ +import unittest +from game.common.hitbox import Hitbox +from game.common.moving.damaging.damaging_object import DamagingObject +from game.common.stats import GameStats +from game.common.hitbox import Hitbox + + +class TestHitboxObject(unittest.TestCase): + def setUp(self): + self.hitbox = Hitbox(5,5,(5,5)) + + def test_set_get_heigth_valid(self): + self.hitbox.height = 10 + self.assertEqual(self.hitbox.height, 10) + + def test_set_get_heigth_valid(self): + self.hitbox.height = 7 + self.assertEqual(self.hitbox.height, 7) + + def test_set_get_height_invalid_low(self): + # Checks if an exception is raised by an Illegal set + # Lambda is needed because the property set isn't considered a function (which assertRaises takes as an argument) + self.assertRaises(Exception, lambda : {self.hitbox.height(10)}) + + def test_set_get_width_invalid_low(self): + self.assertRaises(Exception, lambda : self.hitbox.width(10)) + + def test_set_get_tuple_x_boundary_low(self): + self.assertRaises(Exception, lambda : self.hitbox.position((10, 10))) + + def test_set_get_tuple_y_boundary_low(self): + self.hitbox.position = (10,0) + self.assertEqual(self.hitbox.position[0], 10) + self.assertEqual(self.hitbox.position[1], 0) + + def test_damaging_obj_parent_params(self): + self.assertIsNotNone(self.hitbox.id) + self.assertIsNotNone(self.hitbox.object_type) + +if __name__ == '__main__': + unittest.main \ No newline at end of file diff --git a/game/test_suite/tests/objects/test_initialization.py b/game/test_suite/tests/objects/test_initialization.py index a10c8183..2eb88c23 100644 --- a/game/test_suite/tests/objects/test_initialization.py +++ b/game/test_suite/tests/objects/test_initialization.py @@ -2,6 +2,7 @@ # Always remember to add the proper details to the __init__.py file in the 'tests' folder # to insure your tests are run. +from game.common.hitbox import Hitbox from game.common.map_object import MapObject import unittest from game.common.items.gun import Gun @@ -23,8 +24,8 @@ class TestInit(unittest.TestCase): # Your test class is a subclass of unittest. def setUp(self): # This method is used to set up anything you wish to test prior to every test method below. self.gun = Gun(gun_type=GunType.assault_rifle, level= GunLevel.level_one) - self.item = Item(coordinates={'x': 0, 'y':1}, hitbox={'width': 10, 'height':10} ) - self.damaging = DamagingObject(coordinates={'x': 0, 'y':1}, hitbox={'width': 10, 'height':10}, heading=1, speed=10 ) + self.item = Item(hitbox=Hitbox(10,10,(10,10))) + self.damaging = DamagingObject(hitbox=Hitbox(10,10,(10,10)), heading=1, speed=10 ) self.movObj = MovingObject(1, 10) self.shooter = Shooter() self.action = Action() @@ -32,7 +33,7 @@ def setUp(self): # This method is used to set up anything you wish to test prio self.gameObj = GameObject() self.map = MapObject() self.player = Player() - self.wall = Wall(coordinates={'x': 0, 'y':1}, hitbox={'width': 10, 'height':10} ) + self.wall = Wall(hitbox=Hitbox(10,10,(10,10))) self.grnObj = Grenade(heading=1, speed=1, range=10, damage=10) def testObjectInit(self): diff --git a/game/test_suite/tests/objects/test_moving_object.py b/game/test_suite/tests/objects/test_moving_object.py index 6167053b..7440cceb 100644 --- a/game/test_suite/tests/objects/test_moving_object.py +++ b/game/test_suite/tests/objects/test_moving_object.py @@ -6,6 +6,7 @@ from unittest.case import FunctionTestCase from game.common.moving.moving_object import MovingObject from game.common.stats import GameStats +from game.common.hitbox import Hitbox import math @@ -57,11 +58,10 @@ def test_set_get_speed_boundary_low(self): self.assertEqual(self.movObj.speed, 0) def test_moving_obj_parent_params(self): - test_mov = MovingObject(1, 10, health=1, coordinates=[{'x': 450, 'y': 450}, {'x': 50, 'y': 50}], hitbox={'width': 10, 'height': 10}, collidable=True) - self.assertIsNotNone(test_mov.coordinates) + test_mov = MovingObject(1, 10, health=1, hitbox= Hitbox(10,10,(10,10)), collidable=True) + self.assertIsNotNone(test_mov.hitbox.position) self.assertIsNotNone(test_mov.hitbox) self.assertIsNotNone(test_mov.collidable) - self.assertIsNone(self.movObj.coordinates) self.assertIsNone(self.movObj.hitbox) self.assertIsNone(self.movObj.collidable) diff --git a/game/test_suite/tests/objects/test_shooter.py b/game/test_suite/tests/objects/test_shooter.py index 5e93ce72..3eedc2ac 100644 --- a/game/test_suite/tests/objects/test_shooter.py +++ b/game/test_suite/tests/objects/test_shooter.py @@ -26,7 +26,7 @@ def test_inventory_guns(self): def test_inventory_upgrades(self): self.assertTrue(self.shooter.has_empty_slot('upgrades')) - test_upgrade = Upgrade(None, None, None, None) + test_upgrade = Upgrade(None, None, None) for slot in self.shooter.inventory['upgrades']: self.shooter.append_inventory(test_upgrade) From 9409ea63dea2bb6e85111fb4952f712358d57a82 Mon Sep 17 00:00:00 2001 From: Sean Hagen Date: Sat, 19 Jun 2021 20:04:01 -0500 Subject: [PATCH 2/8] Fixed error in corner property. Added more unit tests --- game/common/hitbox.py | 8 +- .../tests/objects/test_hitbox_object.py | 80 ++++++++++++++++--- 2 files changed, 72 insertions(+), 16 deletions(-) diff --git a/game/common/hitbox.py b/game/common/hitbox.py index 7e2ac99f..cc5d29d6 100644 --- a/game/common/hitbox.py +++ b/game/common/hitbox.py @@ -26,19 +26,19 @@ def position(self): @property def topLeft(self): - return (self.x - (self.width/2), self.y + (self.height/2)) + return (self.position[0] - (self.width/2), self.position[1] + (self.height/2)) @property def topRight(self): - return (self.x + (self.width/2), self.y + (self.height/2)) + return (self.position[0] + (self.width/2), self.position[1] + (self.height/2)) @property def bottomLeft(self): - return (self.x - (self.width/2), self.y - (self.height/2)) + return (self.position[0] - (self.width/2), self.position[1] - (self.height/2)) @property def bottomRight(self): - return (self.x + (self.width/2), self.y - (self.height/2)) + return (self.position[0] + (self.width/2), self.position[1] - (self.height/2)) # set height between 0 and max @height.setter diff --git a/game/test_suite/tests/objects/test_hitbox_object.py b/game/test_suite/tests/objects/test_hitbox_object.py index a9700af4..e2aa2401 100644 --- a/game/test_suite/tests/objects/test_hitbox_object.py +++ b/game/test_suite/tests/objects/test_hitbox_object.py @@ -7,7 +7,7 @@ class TestHitboxObject(unittest.TestCase): def setUp(self): - self.hitbox = Hitbox(5,5,(5,5)) + self.hitbox = Hitbox(2,2,(0,0)) def test_set_get_heigth_valid(self): self.hitbox.height = 10 @@ -17,25 +17,81 @@ def test_set_get_heigth_valid(self): self.hitbox.height = 7 self.assertEqual(self.hitbox.height, 7) - def test_set_get_height_invalid_low(self): - # Checks if an exception is raised by an Illegal set - # Lambda is needed because the property set isn't considered a function (which assertRaises takes as an argument) - self.assertRaises(Exception, lambda : {self.hitbox.height(10)}) - - def test_set_get_width_invalid_low(self): - self.assertRaises(Exception, lambda : self.hitbox.width(10)) + def test_set_get_tuple_x_valid(self): + self.hitbox.position = (50,10) + self.assertEqual(self.hitbox.position, (50,10)) - def test_set_get_tuple_x_boundary_low(self): - self.assertRaises(Exception, lambda : self.hitbox.position((10, 10))) + def test_set_get_tuple_y_valid(self): + self.hitbox.position = (10,50) + self.assertEqual(self.hitbox.position, (10,50)) def test_set_get_tuple_y_boundary_low(self): self.hitbox.position = (10,0) - self.assertEqual(self.hitbox.position[0], 10) - self.assertEqual(self.hitbox.position[1], 0) + self.assertEqual(self.hitbox.position, (10,0)) + + def test_set_get_tuple_x_boundary_high(self): + self.hitbox.position = (GameStats.game_board_width,10) + self.assertEqual(self.hitbox.position, (GameStats.game_board_width,10)) + + # def test_set_get_tuple_X_invalid_high(self): + # self.hitbox.position = (10,GameStats.game_board_height) + # self.assertEqual(self.hitbox.position, (10,GameStats.game_board_height)) + + # def test_set_get_tuple_y_invalid_high(self): + # self.hitbox.position = (10,GameStats.game_board_height) + # self.assertEqual(self.hitbox.position, (10,GameStats.game_board_height)) + + def test_topLeft_corner(self): + self.assertEqual(self.hitbox.topLeft, (-1,1)) + + def test_topLeft_corner(self): + self.assertEqual(self.hitbox.topRight, (1,1)) + + def test_topLeft_corner(self): + self.assertEqual(self.hitbox.bottomLeft, (-1,-1)) + + def test_topLeft_corner(self): + self.assertEqual(self.hitbox.bottomRight, (1,-1)) + + def test_topLeft_corner_alt(self): + self.hitbox.position = (10,10) + self.assertEqual(self.hitbox.topLeft, (9,11)) + + def test_topLeft_corner_alt(self): + self.hitbox.position = (10,10) + self.assertEqual(self.hitbox.topRight, (11,11)) + + def test_topLeft_corner_alt(self): + self.hitbox.position = (10,10) + self.assertEqual(self.hitbox.bottomLeft, (9,9)) + + def test_topLeft_corner_alt(self): + self.hitbox.position = (10,10) + self.assertEqual(self.hitbox.bottomRight, (11,9)) def test_damaging_obj_parent_params(self): self.assertIsNotNone(self.hitbox.id) self.assertIsNotNone(self.hitbox.object_type) + + + # Needs a rework + + # def test_set_get_height_invalid_low(self): + # # Checks if an exception is raised by an Illegal set + # # Lambda is needed because the property set isn't considered a function (which assertRaises takes as an argument) + # self.assertRaises(Exception, lambda : {self.hitbox.height(10)}) + + # def test_set_get_width_invalid_low(self): + # self.assertRaises(Exception, lambda : self.hitbox.width(10)) + + # def test_set_get_tuple_x_boundary_low(self): + # self.assertRaises(Exception, lambda self.hitbox.position((10, 10))) + + # def test_set_get_tuple_x_invalid_high(self): + # try: + # self.hitbox.position = (GameStats.game_board_width,10) + # except: + # self.assertTrue(True) if __name__ == '__main__': unittest.main \ No newline at end of file From 6776d1200130fa5c4076aa51a0851ea43da46a20 Mon Sep 17 00:00:00 2001 From: amanda-f-ndsu <71937870+amanda-f-ndsu@users.noreply.github.com> Date: Sun, 20 Jun 2021 17:16:09 -0500 Subject: [PATCH 3/8] pulled in hitbox object and started work on collision algorithm --- game/utils/collision_detection.py | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 game/utils/collision_detection.py diff --git a/game/utils/collision_detection.py b/game/utils/collision_detection.py new file mode 100644 index 00000000..437dcb26 --- /dev/null +++ b/game/utils/collision_detection.py @@ -0,0 +1,20 @@ +from game.common.hitbox import Hitbox +from game.common.stats import GameStats +from game.common.enums import * + +checkCollision(hitbox_one, hitbox_two): + if (hitbox_one.topLeft[0] < hitbox_two.topRight[0] and hitbox_one.topRight[0] > hitbox_two.topLeft[0] + and hitbox_one.bottomLeft[1] < hitbox_two.bottomLeft[1] + and hitbox_one.bottomRight[1] > hitbox_two.bottomRight[1]): + return False + else: + return True + #RectA.X1 < RectB.X2 && RectA.X2 > RectB.X1 && RectA.Y1 < RectB.Y2 && RectA.Y2 > RectB.Y1 + + #Cond1. If A's left edge is to the right of the B's right edge, - then A is Totally to right Of B + #Cond2. If A's right edge is to the left of the B's left edge, - then A is Totally to left Of B + #Cond3. If A's top edge is below B's bottom edge, - then A is Totally below B + #Cond4. If A's bottom edge is above B's top edge, - then A is Totally above B + #So condition for Non-Overlap is #Cond1 Or Cond2 Or Cond3 Or Cond4 + + \ No newline at end of file From e6b0ce842f549f7ca9550a8959a9b8cee460a8d8 Mon Sep 17 00:00:00 2001 From: amanda-f-ndsu <71937870+amanda-f-ndsu@users.noreply.github.com> Date: Thu, 24 Jun 2021 21:17:23 -0500 Subject: [PATCH 4/8] Created test cases and continued work on collision algorithm --- game/test_suite/tests/__init__.py | 4 ++- game/test_suite/tests/collision_test.py | 38 +++++++++++++++++++++++++ game/utils/collision_detection.py | 26 +++++++---------- 3 files changed, 52 insertions(+), 16 deletions(-) create mode 100644 game/test_suite/tests/collision_test.py diff --git a/game/test_suite/tests/__init__.py b/game/test_suite/tests/__init__.py index 35683e52..d853dda3 100644 --- a/game/test_suite/tests/__init__.py +++ b/game/test_suite/tests/__init__.py @@ -8,6 +8,7 @@ from game.test_suite.tests.objects.test_initialization import TestInit from game.test_suite.tests.objects.test_shooter import TestShooterObject from game.test_suite.tests.objects.test_hitbox_object import TestHitboxObject +from game.test_suite.tests.collision_test import TestCollision __all__ = [ 'TestGameBoard', @@ -16,5 +17,6 @@ 'TestShooterObject', 'TestGrenade', 'TestInit', - 'TestHitboxObject' + 'TestHitboxObject', + 'TestCollision' ] \ No newline at end of file diff --git a/game/test_suite/tests/collision_test.py b/game/test_suite/tests/collision_test.py new file mode 100644 index 00000000..d7eb0aa3 --- /dev/null +++ b/game/test_suite/tests/collision_test.py @@ -0,0 +1,38 @@ +import unittest +from game.common.hitbox import Hitbox +from game.utils.collision_detection import checkCollision + +class TestCollision(unittest.TestCase): + + def setUp(self): + self.hitOne = Hitbox(10,10,(5,5)) + self.hitTwo = Hitbox(25,25,(10,10)) + + + def test_collision_true(self): + self.hitOne.position = (5.0,5.0) + self.hitOne.height = 5 + self.hitOne.width = 8 + + self.hitTwo.position = (5.0,6.0) + self.hitTwo.height = 5 + self.hitTwo.width = 8 + + + self.assertTrue(checkCollision(self.hitOne,self.hitTwo)) + + print(hitOne.topLeft) + + def test_collision_false(self): + self.hitOne.position = (5,5) + self.hitOne.height = 5 + self.hitOne.width = 8 + + self.hitTwo.position = (20,20) + self.hitTwo.height = 5 + self.hitTwo.width = 8 + + self.assertFalse(checkCollision(self.hitOne,self.hitTwo)) + +if __name__ == '__main__': + unittest.main \ No newline at end of file diff --git a/game/utils/collision_detection.py b/game/utils/collision_detection.py index 437dcb26..19b77560 100644 --- a/game/utils/collision_detection.py +++ b/game/utils/collision_detection.py @@ -2,19 +2,15 @@ from game.common.stats import GameStats from game.common.enums import * -checkCollision(hitbox_one, hitbox_two): - if (hitbox_one.topLeft[0] < hitbox_two.topRight[0] and hitbox_one.topRight[0] > hitbox_two.topLeft[0] - and hitbox_one.bottomLeft[1] < hitbox_two.bottomLeft[1] - and hitbox_one.bottomRight[1] > hitbox_two.bottomRight[1]): - return False +def checkCollision(hitbox_one, hitbox_two): + if (hitbox_one.topLeft[0] < hitbox_two.topRight[0] and + hitbox_one.topRight[0] > hitbox_two.topLeft[0] and + hitbox_one.topLeft[1] < hitbox_two.bottomLeft[1] and + hitbox_one.bottomRight[1] > hitbox_two.topRight[1]): + + # if (RectA.X1 < RectB.X2 && RectA.X2 > RectB.X1 && + # RectA.Y1 < RectB.Y2 && RectA.Y2 > RectB.Y1) + return True else: - return True - #RectA.X1 < RectB.X2 && RectA.X2 > RectB.X1 && RectA.Y1 < RectB.Y2 && RectA.Y2 > RectB.Y1 - - #Cond1. If A's left edge is to the right of the B's right edge, - then A is Totally to right Of B - #Cond2. If A's right edge is to the left of the B's left edge, - then A is Totally to left Of B - #Cond3. If A's top edge is below B's bottom edge, - then A is Totally below B - #Cond4. If A's bottom edge is above B's top edge, - then A is Totally above B - #So condition for Non-Overlap is #Cond1 Or Cond2 Or Cond3 Or Cond4 - - \ No newline at end of file + return False + From a71a328e7cd16aa563283332b7ca7f9bdc16fbf2 Mon Sep 17 00:00:00 2001 From: amanda-f-ndsu <71937870+amanda-f-ndsu@users.noreply.github.com> Date: Tue, 29 Jun 2021 14:57:49 -0500 Subject: [PATCH 5/8] made name changes and revised algorithm; added additional unit tests. --- game/test_suite/tests/collision_test.py | 78 ++++++++++++++++++------- game/utils/collision_detection.py | 15 ++--- 2 files changed, 64 insertions(+), 29 deletions(-) diff --git a/game/test_suite/tests/collision_test.py b/game/test_suite/tests/collision_test.py index d7eb0aa3..0112acc8 100644 --- a/game/test_suite/tests/collision_test.py +++ b/game/test_suite/tests/collision_test.py @@ -1,38 +1,76 @@ -import unittest +import unittest from game.common.hitbox import Hitbox -from game.utils.collision_detection import checkCollision +from game.utils.collision_detection import check_collision + class TestCollision(unittest.TestCase): - + def setUp(self): - self.hitOne = Hitbox(10,10,(5,5)) - self.hitTwo = Hitbox(25,25,(10,10)) + self.hitOne = Hitbox(10, 10, (5, 5)) + self.hitTwo = Hitbox(25, 25, (5, 6)) - - def test_collision_true(self): - self.hitOne.position = (5.0,5.0) + """Below are 3 examples of true cases and false cases. The hit boxes were graphed out beforehand + in order to determine if the test cases are behaving as expected.""" + def test_collision_true_one(self): + self.hitOne.position = (5, 5) self.hitOne.height = 5 self.hitOne.width = 8 - - self.hitTwo.position = (5.0,6.0) + + self.hitTwo.position = (5, 6) self.hitTwo.height = 5 self.hitTwo.width = 8 + self.assertTrue(check_collision(self.hitOne, self.hitTwo)) - - self.assertTrue(checkCollision(self.hitOne,self.hitTwo)) - - print(hitOne.topLeft) - - def test_collision_false(self): - self.hitOne.position = (5,5) + def test_collision_false_one(self): + self.hitOne.position = (5, 5) self.hitOne.height = 5 self.hitOne.width = 8 - self.hitTwo.position = (20,20) + self.hitTwo.position = (20, 20) self.hitTwo.height = 5 self.hitTwo.width = 8 + self.assertFalse(check_collision(self.hitOne, self.hitTwo)) + + def test_collision_true_two(self): + self.hitOne.position = (5, 5) + self.hitOne.height = 6 + self.hitOne.width = 4 + + self.hitTwo.position = (1, 3) + self.hitTwo.height = 2 + self.hitTwo.width = 6 + self.assertTrue(check_collision(self.hitOne, self.hitTwo)) + + def test_collision_false_two(self): + self.hitOne.position = (5, 5) + self.hitOne.height = 4 + self.hitOne.width = 4 + + self.hitTwo.position = (20, 20) + self.hitTwo.height = 6 + self.hitTwo.width = 2 + self.assertFalse(check_collision(self.hitOne, self.hitTwo)) + + def test_collision_true_three(self): + self.hitOne.position = (60, 50) + self.hitOne.height = 40 + self.hitOne.width = 80 + + self.hitTwo.position = (20, 20) + self.hitTwo.height = 30 + self.hitTwo.width = 40 + self.assertTrue(check_collision(self.hitOne, self.hitTwo)) + + def test_collision_false_three(self): + self.hitOne.position = (60, 50) + self.hitOne.height = 40 + self.hitOne.width = 80 + + self.hitTwo.position = (20, 20) + self.hitTwo.height = 10 + self.hitTwo.width = 20 + self.assertFalse(check_collision(self.hitOne, self.hitTwo)) - self.assertFalse(checkCollision(self.hitOne,self.hitTwo)) if __name__ == '__main__': - unittest.main \ No newline at end of file + unittest.main diff --git a/game/utils/collision_detection.py b/game/utils/collision_detection.py index 19b77560..3680cc8b 100644 --- a/game/utils/collision_detection.py +++ b/game/utils/collision_detection.py @@ -2,15 +2,12 @@ from game.common.stats import GameStats from game.common.enums import * -def checkCollision(hitbox_one, hitbox_two): - if (hitbox_one.topLeft[0] < hitbox_two.topRight[0] and - hitbox_one.topRight[0] > hitbox_two.topLeft[0] and - hitbox_one.topLeft[1] < hitbox_two.bottomLeft[1] and - hitbox_one.bottomRight[1] > hitbox_two.topRight[1]): - - # if (RectA.X1 < RectB.X2 && RectA.X2 > RectB.X1 && - # RectA.Y1 < RectB.Y2 && RectA.Y2 > RectB.Y1) + +def check_collision(hitbox_one, hitbox_two): + if (hitbox_one.topLeft[0] < hitbox_two.topRight[0] and + hitbox_one.topRight[0] > hitbox_two.topLeft[0] and + hitbox_one.topLeft[1] > hitbox_two.bottomLeft[1] and + hitbox_one.bottomRight[1] < hitbox_two.topRight[1]): return True else: return False - From f24b983f788e5a70c8885ca7f24efc4081abf234 Mon Sep 17 00:00:00 2001 From: amanda-f-ndsu <71937870+amanda-f-ndsu@users.noreply.github.com> Date: Wed, 30 Jun 2021 17:44:22 -0500 Subject: [PATCH 6/8] changed if statement to match changes in hitbox corners --- game/common/hitbox.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/game/common/hitbox.py b/game/common/hitbox.py index cc5d29d6..70827f8a 100644 --- a/game/common/hitbox.py +++ b/game/common/hitbox.py @@ -26,19 +26,19 @@ def position(self): @property def topLeft(self): - return (self.position[0] - (self.width/2), self.position[1] + (self.height/2)) + return (self.position[0] - (self.width/2), self.position[1] - (self.height/2)) @property def topRight(self): - return (self.position[0] + (self.width/2), self.position[1] + (self.height/2)) + return (self.position[0] + (self.width/2), self.position[1] - (self.height/2)) @property def bottomLeft(self): - return (self.position[0] - (self.width/2), self.position[1] - (self.height/2)) + return (self.position[0] - (self.width/2), self.position[1] + (self.height/2)) @property def bottomRight(self): - return (self.position[0] + (self.width/2), self.position[1] - (self.height/2)) + return (self.position[0] + (self.width/2), self.position[1] + (self.height/2)) # set height between 0 and max @height.setter From f0f248e186db5b96150d298d4abad2e57b9e7345 Mon Sep 17 00:00:00 2001 From: Github Actions Date: Tue, 6 Jul 2021 05:12:45 +0000 Subject: [PATCH 7/8] Automated autopep8 fixes --- game/common/hitbox.py | 11 +++++++++-- game/test_suite/tests/collision_test.py | 3 ++- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/game/common/hitbox.py b/game/common/hitbox.py index 3a8d8e05..43c210a4 100644 --- a/game/common/hitbox.py +++ b/game/common/hitbox.py @@ -42,7 +42,13 @@ def bottomRight(self): @property def middle(self): - return (self.position[0] + self.width / 2, self.position[1] + self.height / 2) + return ( + self.position[0] + + self.width / + 2, + self.position[1] + + self.height / + 2) # set height between 0 and max @height.setter @@ -67,7 +73,8 @@ def position(self, val): 0 <= val[1] <= GameStats.game_board_height): self.__position = val else: - raise ValueError("Tried to set an invalid xy position tuple for hitbox") + raise ValueError( + "Tried to set an invalid xy position tuple for hitbox") def to_json(self): data = super().to_json() diff --git a/game/test_suite/tests/collision_test.py b/game/test_suite/tests/collision_test.py index 1756c7a6..8ca3ed68 100644 --- a/game/test_suite/tests/collision_test.py +++ b/game/test_suite/tests/collision_test.py @@ -9,8 +9,9 @@ def setUp(self): self.hitOne = Hitbox(10, 10, (5, 5)) self.hitTwo = Hitbox(25, 25, (5, 6)) - """Below are 3 examples of true cases and false cases. The hit boxes were graphed out beforehand + """Below are 3 examples of true cases and false cases. The hit boxes were graphed out beforehand in order to determine if the test cases are behaving as expected.""" + def test_collision_true_one(self): self.hitOne.position = (5, 5) self.hitOne.height = 5 From 70281c7f1c8253a39605463f13cf02f3cbacfb58 Mon Sep 17 00:00:00 2001 From: amanda-f-ndsu <71937870+amanda-f-ndsu@users.noreply.github.com> Date: Tue, 6 Jul 2021 19:38:45 -0500 Subject: [PATCH 8/8] Made minor syntax changes --- game/test_suite/tests/collision_test.py | 4 ++-- game/utils/collision_detection.py | 7 ++----- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/game/test_suite/tests/collision_test.py b/game/test_suite/tests/collision_test.py index 1756c7a6..00b704f3 100644 --- a/game/test_suite/tests/collision_test.py +++ b/game/test_suite/tests/collision_test.py @@ -9,8 +9,8 @@ def setUp(self): self.hitOne = Hitbox(10, 10, (5, 5)) self.hitTwo = Hitbox(25, 25, (5, 6)) - """Below are 3 examples of true cases and false cases. The hit boxes were graphed out beforehand - in order to determine if the test cases are behaving as expected.""" + # Below are 3 examples of true cases and false cases. The hit boxes were graphed out beforehand + # in order to determine if the test cases are behaving as expected. def test_collision_true_one(self): self.hitOne.position = (5, 5) self.hitOne.height = 5 diff --git a/game/utils/collision_detection.py b/game/utils/collision_detection.py index 034fdd11..29845678 100644 --- a/game/utils/collision_detection.py +++ b/game/utils/collision_detection.py @@ -4,10 +4,7 @@ def check_collision(hitbox_one, hitbox_two): - if (hitbox_one.topLeft[0] < hitbox_two.topRight[0] and + return (hitbox_one.topLeft[0] < hitbox_two.topRight[0] and hitbox_one.topRight[0] > hitbox_two.topLeft[0] and hitbox_one.topLeft[1] < hitbox_two.bottomLeft[1] and - hitbox_one.bottomRight[1] > hitbox_two.topRight[1]): - return True - else: - return False + hitbox_one.bottomRight[1] > hitbox_two.topRight[1])