mrpeed

Members
  • Content count

    38
  • Joined

  • Last visited

Community Reputation

277 Neutral

About mrpeed

  • Rank
    Member

Personal Information

  • Interests
    Art
    Audio
    Design
    Programming
    QA
  1. How Much is Too Much?

    Too much is too much when you are unhappy and not enjoying yourself. Sometimes, we like the idea of doing things but not actually doing them. If you find that doing all those different things is making you unhappy, than you should reconsider how you spend your time. But if you genuinely enjoy what you do, then by all means keep doing it. Well first, if you are talking about improvement you need to make sure you are practicing properly and that you are applying what you practice to actual projects. You can spend lots of time doing things and not get any better because you aren't challenging yourself properly. Also, you can evenly divide your time among the different skills you mentioned to make sure you aren't "suffering" in any area. But as your life gets more busy you will find that you may not have lots of time in a day, and doing everything may no longer be viable. In that case you should probably decide on which skills are more important to you and focus on those. If you focus on everything you may never master one thing, but that may be okay if that's not a goal for you.
  2. Python code review for Snake

    I did a second draft of the game.py file if you want to take a look. I plan to redo my input manager this week. Some questions: 1. I use lots of functions because I find reading something like draw_segment() easier than reading pygame.draw.rect(self.displaysurf, GREEN, segment.rect), even if the function is one line. Also, I find that smaller functions are easy to write tests for which I often do. And if I need to make a change in the future, I would know exactly where a change needs to go. Do you think that approach is overkill for this program or in general? 2. Do you think putting all of my methods in the Game class for a game of this size is bad? Again thanks for taking your time to look at my game! It has been super helpful. #Removed refresh(), terminate(), extend_snake(), draw_extender(), draw_snake() #Capitalized constants #Added spaces #Modified some methods #Now uses lists import sys import random import itertools import pygame import inputmanager FPS = 10 WINDOW_SIZE = (640, 480) CELL_SIZE = (32, 32) START_LOCATION = (320, 224) UP = "up" DOWN = "down" LEFT = "left" RIGHT = "right" BLACK = (0, 0, 0) GREEN = (0, 255, 0) RED = (255, 0, 0) class Segment: def __init__(self, rect, direction=None): self.rect = rect self.direction = direction class Game: def __init__(self): self.direction = None self.extender = None self.cell_locations = self.cell_locations = set( itertools.product( range(0, WINDOW_SIZE[0], CELL_SIZE[0]), range(0, WINDOW_SIZE[1], CELL_SIZE[1]) ) ) self.snake = [Segment(pygame.Rect(START_LOCATION, CELL_SIZE))] pygame.init() self.fps_clock = pygame.time.Clock() self.displaysurf = pygame.display.set_mode(WINDOW_SIZE) pygame.display.set_caption("Snake") def main(self): while True: self.get_input() self.update() self.render() self.fps_clock.tick(FPS) def get_input(self): inputmanager.InputManager.get_events() inputmanager.InputManager.check_for_quit_event() inputmanager.InputManager.update_keyboard_key_state() inputmanager.InputManager.get_keyboard_input() def update(self): self.handle_input() self.update_snake_direction(self.direction) self.move_snake() if self.extender is None: snake_segments_locations = set(segment.rect.topleft for segment in self.snake) location = random.choice(list(self.cell_locations-snake_segments_locations)) ###Need to use set to subtract, and need to convert to list to use random.choice self.extender = pygame.Rect(location, CELL_SIZE) if self.head_segment_collided_with_extender(): self.extender = None self.add_segment_to_snake() if self.game_over(): self.snake = [Segment(pygame.Rect(START_LOCATION, CELL_SIZE))] self.direction = None self.extender = None def handle_input(self): if inputmanager.InputManager.quit: pygame.quit() sys.exit() if (inputmanager.InputManager.keyboard[pygame.K_UP] == inputmanager.InputManager.pressed and self.direction != DOWN): self.direction = UP elif (inputmanager.InputManager.keyboard[pygame.K_DOWN] == inputmanager.InputManager.pressed and self.direction != UP): self.direction = DOWN elif (inputmanager.InputManager.keyboard[pygame.K_LEFT] == inputmanager.InputManager.pressed and self.direction != RIGHT): self.direction = LEFT elif (inputmanager.InputManager.keyboard[pygame.K_RIGHT] == inputmanager.InputManager.pressed and self.direction != LEFT): self.direction = RIGHT def update_snake_direction(self, head_direction): for index in reversed(range(len(self.snake))): self.snake[index].direction = self.snake[index-1].direction self.snake[0].direction = head_direction def move_snake(self): for segment in self.snake: self.move_segment(segment) def move_segment(self, segment): move_amount = { UP: (0, -CELL_SIZE[1]), DOWN: (0, CELL_SIZE[1]), LEFT: (-CELL_SIZE[0], 0), RIGHT: (CELL_SIZE[0], 0) }.get(segment.direction, (0, 0)) segment.rect.move_ip(move_amount) def head_segment_collided_with_extender(self): return self.snake[0].rect.colliderect(self.extender) def add_segment_to_snake(self): topleft = { UP: (self.snake[-1].rect.x, self.snake[-1].rect.y+CELL_SIZE[1]), DOWN: (self.snake[-1].rect.x, self.snake[-1].rect.y-CELL_SIZE[1]), LEFT: (self.snake[-1].rect.x+CELL_SIZE[0], self.snake[-1].rect.y), RIGHT: (self.snake[-1].rect.x-CELL_SIZE[0], self.snake[-1].rect.y) }.get(self.snake[-1].direction, (0, 0)) self.snake.append(Segment(pygame.Rect(topleft, CELL_SIZE), self.snake[-1].direction)) def game_over(self): return (self.head_segment_collided_with_self() or self.head_segment_out_of_bounds()) def head_segment_collided_with_self(self): return any([self.snake[0].rect.colliderect(segment) for segment in self.snake[1:]]) def head_segment_out_of_bounds(self): return (self.snake[0].rect.x < 0 or self.snake[0].rect.y < 0 or self.snake[0].rect.x >= WINDOW_SIZE[0] or self.snake[0].rect.y >= WINDOW_SIZE[1]) def render(self): self.displaysurf.fill(BLACK) for segment in self.snake: pygame.draw.rect(self.displaysurf, GREEN, segment.rect) if self.extender is not None: pygame.draw.rect(self.displaysurf, RED, self.extender) pygame.display.update() if __name__ == "__main__": game = Game() game.main() @Alberth game.py
  3. Python code review for Snake

    Thanks so much for the reply, I'm going to review your comments and I'll get back with any additional questions.
  4. I wrote Snake in Python 3 using Pygame and was wondering if anyone can do a code review of it? If this is the appropriate fourm to post such a thing? Some things to mention: 1. I realize I could have used a dict in the place of my Segment class, but I decided to go with the class because it looked more clean to me. 2. I used recursion heavily, though I could have used a list instead. I decided to do it recursively for practice and fun (I don't use recursion often). 3. I don't have doc strings for any of my functions. 4. I probably could have used my get_all_snake_segment_locations function to avoid recursion. 5. I set fps to 10 to limit the speed of the game. Is this a bad way to do such a thing? 6. I attached an input manager I created and unit tests for my game for completeness. Though, I'm only asking the actual game to be reviewed, if you want to look at those you can. Also, note the unit tests are not complete yet for several functions I changed. 7. I really appreciate anyone who takes the time to give me feedback of any kind. This fourm has been a huge help to me and I'm grateful for everyone's insight! import sys import random import itertools import pygame import inputmanager class Segment: def __init__(self, rect, direction=None, parent=None, child=None): self.rect = rect self.direction = direction self.parent = parent self.child = child class Game: def __init__(self): pygame.init() self.fps_clock = pygame.time.Clock() self.fps = 10 self.window_size = (640, 480) self.displaysurf = pygame.display.set_mode(self.window_size) pygame.display.set_caption("Snake") self.cell_size = (32, 32) self.start_location = (320, 224) self.head_segment = Segment(pygame.Rect(self.start_location, self.cell_size)) self.up = "up" self.down = "down" self.left = "left" self.right = "right" self.black = (0, 0, 0) self.green = (0, 255, 0) self.red = (255, 0, 0) self.direction = None self.extender = None self.cell_locations = set( itertools.product( range(0, self.window_size[0], self.cell_size[0]), range(0, self.window_size[1], self.cell_size[1]) ) ) def main(self): while True: self.get_input() self.update() self.render() self.fps_clock.tick(self.fps) def get_input(self): inputmanager.InputManager.get_events() inputmanager.InputManager.check_for_quit_event() inputmanager.InputManager.update_keyboard_key_state() inputmanager.InputManager.get_keyboard_input() def update(self): self.handle_input() self.update_snake_direction(self.head_segment, self.direction) self.move_snake(self.head_segment) if self.extender is None: self.add_extender_to_board() if self.head_segment_collided_with_extender(): self.extend_snake() if self.game_over(): self.refresh() def handle_input(self): if inputmanager.InputManager.quit: self.terminate() if (inputmanager.InputManager.keyboard[pygame.K_UP] == inputmanager.InputManager.pressed and self.direction != self.down): self.direction = self.up elif (inputmanager.InputManager.keyboard[pygame.K_DOWN] == inputmanager.InputManager.pressed and self.direction != self.up): self.direction = self.down elif (inputmanager.InputManager.keyboard[pygame.K_LEFT] == inputmanager.InputManager.pressed and self.direction != self.right): self.direction = self.left elif (inputmanager.InputManager.keyboard[pygame.K_RIGHT] == inputmanager.InputManager.pressed and self.direction != self.left): self.direction = self.right def terminate(self): pygame.quit() sys.exit() def update_snake_direction(self, segment, parent_direction): ###TEST if segment.child is not None: self.update_snake_direction(segment.child, parent_direction) if segment.parent is None: segment.direction = parent_direction else: segment.direction = segment.parent.direction def move_snake(self, segment): self.move_segment(segment) if segment.child is not None: self.move_snake(segment.child) def move_segment(self, segment): if segment.direction == self.up: segment.rect.move_ip(0, -self.cell_size[1]) elif segment.direction == self.down: segment.rect.move_ip(0, self.cell_size[1]) elif segment.direction == self.left: segment.rect.move_ip(-self.cell_size[0], 0) elif segment.direction == self.right: segment.rect.move_ip(self.cell_size[0], 0) def add_extender_to_board(self): snake_segments_locations = set(self.get_all_snake_segment_locations(self.head_segment)) location = random.choice(list(self.cell_locations-snake_segments_locations)) self.extender = pygame.Rect(location, self.cell_size) def get_all_snake_segment_locations(self, segment): yield segment.rect.topleft if segment.child is not None: yield from self.get_all_snake_segment_locations(segment.child) def head_segment_collided_with_extender(self): return self.head_segment.rect.colliderect(self.extender) def extend_snake(self): self.extender = None self.add_segment_to_snake(self.head_segment) def add_segment_to_snake(self, segment): if segment.child is None: if segment.direction == self.up: topleft = (segment.rect.x, segment.rect.y+self.cell_size[1]) elif segment.direction == self.down: topleft = (segment.rect.x, segment.rect.y-self.cell_size[1]) elif segment.direction == self.left: topleft = (segment.rect.x+self.cell_size[0], segment.rect.y) elif segment.direction == self.right: topleft = (segment.rect.x-self.cell_size[0], segment.rect.y) segment.child = Segment( pygame.Rect(topleft, self.cell_size), segment.direction, segment ) else: self.add_segment_to_snake(segment.child) def game_over(self): return any([ self.head_segment_collided_with_self(self.head_segment), self.head_segment_out_of_bounds() ]) def head_segment_collided_with_self(self, segment): if segment.child is not None: if self.head_segment.rect.colliderect(segment.child.rect): return True else: return self.head_segment_collided_with_self(segment.child) return False def head_segment_out_of_bounds(self): if (self.head_segment.rect.x < 0 or self.head_segment.rect.y < 0 or self.head_segment.rect.x >= self.window_size[0] or self.head_segment.rect.y >= self.window_size[1]): return True return False def refresh(self): self.head_segment = Segment(pygame.Rect(self.start_location, self.cell_size)) self.direction = None self.extender = None def render(self): self.displaysurf.fill(self.black) self.draw_snake(self.head_segment) if self.extender is not None: self.draw_extender() pygame.display.update() def draw_snake(self, segment): pygame.draw.rect(self.displaysurf, self.green, segment.rect) if segment.child is not None: self.draw_snake(segment.child) def draw_extender(self): pygame.draw.rect(self.displaysurf, self.red, self.extender) if __name__ == "__main__": game = Game() game.main() test_game.py inputmanager.py game.py
  5. Lets say I have a character with a shooting animation that plays every time I hit the space key. I need to block input to assure that the animation finishes before another action is taken. What is a common approach to doing this? Right now, I'm using a variable that is a set to the total number of seconds the animation is. When the variable is zero player input is allowed again. Is there a better way? I'm using Python and Pygame for a 2D game.
  6. Thanks for reply, I'll look more into mocking.
  7. Currently, I'm working on recreating connect four for a portfolio of simple games. I'm also writing unit tests for all the code I can. Some of my code functions though only call other functions, or only draw to the screen... #EXAMPLES... def draw_refresh_button_text(self): font = pygame.font.Font(None, 24) text = font.render("Refresh", True, self.black) self.displaysurf.blit( text, ( self.refresh_button_rect.centerx-self.refresh_button_rect.centerx/self.refresh_button_rect.y, self.refresh_button_rect.centery-self.refresh_button_rect.centery/self.refresh_button_rect.x ) ) def draw_disks(self): for column in self.grid: for disk in column: pygame.draw.ellipse(self.displaysurf, disk['color'], disk['rect']) #ONLY CALLS OTHER FUNCTIONS... def get_input(self): inputmanager.InputManager.get_events() inputmanager.InputManager.check_for_quit_event() inputmanager.InputManager.update_keyboard_key_state() inputmanager.InputManager.get_keyboard_input() inputmanager.InputManager.update_mouse_button_state() inputmanager.InputManager.get_mouse_input() How would I go about writing tests for code like this? Should code like this even be tested? Should all code be tested? I'm using the Python unittest module to test. The code here uses Pygame.
  8. Is redrawing the entire screen every frame the common approach? Even if there are areas on the display that haven't changed? I'm using Pygame and I've read that you should only redraw the area of the screen that has been changed for performance reasons, though I'm not sure if this is unique to Pygame or even relevant to to Pygame anymore.
  9. Game Development Books/Videos (Mapt/Packt)

    Though not from Mapt/Packt, this is a good book to give a read (free online!). http://gameprogrammingpatterns.com/contents.html
  10. This function detects if a gameobject has moved or has had its sprite changed. It then calls one of two functions (or none), to draw a piece of the games background over the gameobjects current location or previous location. Should functions like this be avoided? Is this good or bad design? Does it violate SRP? Also, how would something like this be unit tested? #clean up the display... def blit_background_over_dirty_rects(self): for gameobject, data in self.relevant_data.items(): if data['location'] != gameobject.rect.topleft: #gameobject moved... self._blit_background_over_gameobjects_previous_location(gameobject) elif data['sprite'] != gameobject.sprite_manager.current_sprite: #gameobjects sprite changed... self._blit_background_over_gameobjects_current_location(gameobject)
  11. If I see no reason for something to be a property over a method should I favor making it a property or a method? For example, I can see no benefit in doing a "full" property over a "is_full() method", other than not having to write (). I can understand if full started as a regular variable and was expanded to a property later, but it wasn't. So in that case what would you say?
  12. Yes, I am initializing that in the __init__ method. And makes sense, I decided to remove the is_full() check from each method and have the inventory size enforced outside of the inventory class. This also removed a lot of clutter from my methods which is good. I was wondering if you can clarify when to use properties? I tend to avoid them because I feel like they are misleading. For example, @property def full(self): return len(self.items) >= self.max_inventory_size Doesn't this hide the fact that this is a method? Would a user not think this a regular variable and possibly try to assign to it? I have other methods labeled get and set throughout my code base that changes behavior within different classes. Would it be misleading to convert those to properties? Another example, would it be to misleading to make this a next_sprite property? def get_next_sprite(self): if self.current_count == self.max_count: #Time to switch sprites... self.current_count = 0 self.current_sprite = next(self.sprite_reel) else: #Increment and return the same sprite from last frame... self.current_count += 1 return self.current_sprite
  13. Makes sense to use a dict actually, not sure why I went with a list. Curious to why you decided to make this a property? Also, what's your take on using an exception? As in my second example.
  14. Here are 3 versions of a method from my inventory class that adds an item to the given inventory (or increases the items quantity). From a Python perspective, which approach would be best? Each method is followed by a sample of how I would use it in practice. I've been pretty interested in code design/architecture lately and I've been trying to get better at Python, so it may seem like a silly question but I'm just curious what you people think. Currently, I'm using approach #1, but I find it misleading that you could add an item and not actually have an item added. And if there's another approach not listed here that would be good please let me know. #1 prevent adding item if is_full(): def add_item(self, item): i = next((i for i in self.items if i.tag == item.tag), None) #Duplicate item... if i is not None: i.quantity += item.quantity elif not self.is_full(): self.items.append(item) player_inventory.add_item(item) #2 throw exception if is_full() and handle outside method: def add_item(self, item): i = next((i for i in self.items if i.tag == item.tag), None) if i is not None: i.quantity += item.quantity elif not self.is_full(): self.items.append(item) else: raise InvetoryFullException("Inventory can only hold 8 items.") try: player_inventory.add_item(item) except InvetoryFullException: pass #3 check if inventory is_full() outside of method: def add_item(self, item): i = next((i for i in self.items if i.tag == item.tag), None) if i is not None: i.quantity += item.quantity else: self.items.append(item) if not player_inventory.is_full(): player_inventory.items.append(item)
  15. Is there a way to combine all tiles into one so I can do something like self.displaysurf.blit(self.background.subsurface(gameobject), gameobject_location). How do I subsurface multiple tiles? Right now I just get a subsurface of my background image at the gameobjects locations and of the gameobjects size. I've heard of it and will definitely check it out in the future. Is Pyglet more difficult than Pygame? Is it just for graphics?