From bc72fbf25fa151bce1fd526fe5ca4d98c6bac293 Mon Sep 17 00:00:00 2001 From: Raphael McSinyx Date: Thu, 19 Oct 2017 15:24:56 +0700 Subject: Add dynamic framerate --- brutalmaze/characters.py | 43 ++++++++++++++------------- brutalmaze/constants.py | 8 ++--- brutalmaze/main.py | 14 +++++++-- brutalmaze/maze.py | 77 ++++++++++++++++++++++++++---------------------- 4 files changed, 80 insertions(+), 62 deletions(-) diff --git a/brutalmaze/characters.py b/brutalmaze/characters.py index 8dc3209..250d9be 100644 --- a/brutalmaze/characters.py +++ b/brutalmaze/characters.py @@ -62,16 +62,16 @@ def sign(n): class Hero: """Object representing the hero.""" - def __init__(self, surface): + def __init__(self, surface, fps): self.surface = surface w, h = self.surface.get_width(), self.surface.get_height() self.x, self.y = w >> 1, h >> 1 self.angle, self.color = pi / 4, TANGO['Aluminium'] self.R = int((w * h / sin(pi*2/3) / 624) ** 0.5) - self.wound = 0.0 - self.speed = FPS // len(self.color) + self.spin_speed = int(round(fps / len(self.color))) self.spin_queue, self.slashing = deque(), False + self.wound = 0.0 def get_color(self): """Return the color of the hero based on the amount of wounds.""" @@ -82,25 +82,25 @@ class Hero: each spin. """ if self.slashing and not self.spin_queue: - if hold: self.spin_queue.extend([0] * (self.speed >> 1)) - self.spin_queue.extend([randsign()] * self.speed) + if hold: self.spin_queue.extend([0] * (self.spin_speed >> 1)) + self.spin_queue.extend([randsign()] * self.spin_speed) def draw(self, color=None): """Draw the hero.""" trigon = regpoly(3, self.R, self.angle, self.x, self.y) fill_aapolygon(self.surface, trigon, color or self.get_color()) - def update(self): + def update(self, fps): """Update the hero.""" - self.wound -= HEAL_SPEED / len(self.color) / self.speed + self.spin_speed = int(round(fps / (len(self.color)-self.wound))) + self.wound -= HEAL_SPEED / len(self.color) / (self.spin_speed or 1) if self.wound < 0: self.wound = 0.0 - self.speed = int(FPS / (len(self.color)-self.wound)) self.slash(hold=True) direction = self.spin_queue.popleft() if self.spin_queue else 0 self.draw(color=BG_COLOR) if direction: - self.angle += direction * pi * 2 / 3 / self.speed + self.angle += direction * pi * 2 / 3 / self.spin_speed else: # Follow the mouse cursor x, y = pygame.mouse.get_pos() @@ -116,27 +116,28 @@ class Hero: class Enemy: """Object representing an enemy.""" - def __init__(self, surface, maze, kind, x, y): + def __init__(self, surface, fps, maze, kind, x, y): self.surface, self.maze = surface, maze self.angle, self.color = pi / 4, TANGO[kind] self.x, self.y = x, y self.maze[x][y] = ENEMY self.awake = False + self.move_speed = fps / MOVE_SPEED self.offsetx = self.offsety = 0 + self.spin_speed = int(round(fps / len(self.color))) self.spin_queue = [] - self.speed = FPS // len(self.color) self.wound = 0.0 def pos(self, distance, middlex, middley): """Return coordinate of the center of the enemy.""" x, y = pos(self.x, self.y, distance, middlex, middley) - step = distance / MOVE_SPEED + step = distance / self.move_speed return x + self.offsetx*step, y + self.offsety*step def draw(self, distance, middlex, middley, color): """Draw the enemy, given distance between grids and the middle grid.""" - radious = distance/SQRT2 - (self.awake and 2) + radious = distance/SQRT2 - self.awake*2 square = regpoly(4, radious, self.angle, *self.pos(distance, middlex, middley)) fill_aapolygon(self.surface, square, color) @@ -149,7 +150,7 @@ class Enemy: self.y %= len(self.maze) self.maze[self.x][self.y] = ENEMY - def move(self): + def move(self, fps): """Handle the movement of the enemy. Return True if it moved, False otherwise. @@ -161,25 +162,27 @@ class Enemy: self.offsety -= sign(self.offsety) return True + self.move_speed = fps / MOVE_SPEED directions = [(sign(MIDDLE - self.x), 0), (0, sign(MIDDLE - self.y))] shuffle(directions) for x, y in directions: if (x or y) and self.maze[self.x + x][self.y + y] == EMPTY: - self.offsetx = x * (1-MOVE_SPEED) - self.offsety = y * (1-MOVE_SPEED) + self.offsetx = round(x * (1 - self.move_speed)) + self.offsety = round(y * (1 - self.move_speed)) self.maze[self.x][self.y] = EMPTY self.place(x, y) return True return False - def update(self, distance, middlex, middley): + def update(self, fps, distance, middlex, middley): """Update the enemy.""" if self.awake: self.draw(distance, middlex, middley, BG_COLOR) - if not self.spin_queue and not self.move(): - self.spin_queue.extend([randsign()] * self.speed) + if not self.spin_queue and not self.move(fps): + self.spin_speed = int(round(fps / len(self.color))) + self.spin_queue.extend([randsign()] * self.spin_speed) if self.spin_queue: - self.angle += self.spin_queue.pop() * pi / 2 / self.speed + self.angle += self.spin_queue.pop() * pi / 2 / self.spin_speed self.draw(distance, middlex, middley, self.color[int(self.wound)] if self.awake else FG_COLOR) diff --git a/brutalmaze/constants.py b/brutalmaze/constants.py index 427226e..e87a33b 100644 --- a/brutalmaze/constants.py +++ b/brutalmaze/constants.py @@ -28,15 +28,15 @@ ICON = image.load(resource_filename('brutalmaze', 'icon.png')) SQRT2 = 2 ** 0.5 GOLDEN_MEAN = 5**0.5/2 + 0.5 -FPS = 30 -SIZE = 400, 400 -MAZE_SIZE = 10 +INIT_FPS = 30.0 +SIZE = 640, 480 +MAZE_SIZE = 12 ROAD_WIDTH = 5 CELL_WIDTH = ROAD_WIDTH * 2 MIDDLE = (MAZE_SIZE + MAZE_SIZE%2 - 1)*ROAD_WIDTH + ROAD_WIDTH//2 LAST_ROW = (MAZE_SIZE-1) * ROAD_WIDTH * 2 INIT_SCORE = 208.2016 -MOVE_SPEED = 5 # step/grid +MOVE_SPEED = 5 # grid/s HEAL_SPEED = 1.0 # HP/s EMPTY, WALL, HERO, ENEMY = range(4) diff --git a/brutalmaze/main.py b/brutalmaze/main.py index 40b4116..73d4427 100644 --- a/brutalmaze/main.py +++ b/brutalmaze/main.py @@ -17,6 +17,8 @@ # # Copyright (C) 2017 Nguyễn Gia Phong +from collections import deque + import pygame from .constants import * @@ -28,7 +30,8 @@ def main(): pygame.init() pygame.display.set_icon(ICON) pygame.fastevent.init() - maze, going, clock = Maze(SIZE), True, pygame.time.Clock() + maze, clock = Maze(SIZE, INIT_FPS), pygame.time.Clock() + fps, flash_time, going = INIT_FPS, deque(), True while going: events = pygame.fastevent.get() for event in events: @@ -66,6 +69,11 @@ def main(): maze.hero.slashing = False elif event.type == VIDEORESIZE: maze.resize(event.w, event.h) - maze.update() - clock.tick(FPS) + if len(flash_time) > 10: + new_fps = 10000.0 / (flash_time[-1] - flash_time[0]) + fps += -1 if new_fps < fps else 5 + flash_time.popleft() + maze.update(fps) + flash_time.append(pygame.time.get_ticks()) + clock.tick(fps) pygame.quit() diff --git a/brutalmaze/maze.py b/brutalmaze/maze.py index 4b2abdb..8e3a5ad 100644 --- a/brutalmaze/maze.py +++ b/brutalmaze/maze.py @@ -63,24 +63,25 @@ def new_column(): class Maze: """Object representing the maze, including the characters.""" - def __init__(self, size): + def __init__(self, size, fps): self.w, self.h = size + self.fps, self.speed = fps, fps / MOVE_SPEED self.surface = pygame.display.set_mode(size, RESIZABLE) self.distance = (self.w * self.h / 416) ** 0.5 - self.step = self.distance / MOVE_SPEED + self.step = self.distance / self.speed self.middlex, self.middley = self.x, self.y = self.w >> 1, self.h >> 1 w, h = (int((i/self.distance+2) / 2) for i in size) self.rangex = range(MIDDLE - w, MIDDLE + w + 1) self.rangey = range(MIDDLE - h, MIDDLE + h + 1) - self.right = self.down = self.offsetx = self.offsety = 0 + self.offsetx = self.offsety = 0.0 self.score = INIT_SCORE self.map = deque() for _ in range(MAZE_SIZE): self.map.extend(new_column()) - self.rotatex = self.rotatey = 0 + self.right = self.down = self.rotatex = self.rotatey = 0 self.enemies = [] self.add_enemy() - self.hero = Hero(self.surface) + self.hero = Hero(self.surface, fps) self.map[MIDDLE][MIDDLE] = HERO self.slashd = self.hero.R + self.distance/SQRT2 self.draw() @@ -96,7 +97,7 @@ class Maze: if all(self.map[x + a][y + b] == WALL for a, b in ADJACENT_GRIDS): continue self.enemies.append( - Enemy(self.surface, self.map, choice(ENEMIES), x, y)) + Enemy(self.surface, self.fps, self.map, choice(ENEMIES), x, y)) walls.remove((x, y)) def draw(self): @@ -130,11 +131,11 @@ class Maze: """Rotate the maze by (x, y).""" for enemy in self.enemies: self.map[enemy.x][enemy.y] = EMPTY if x: - self.offsetx = 0 + self.offsetx = 0.0 self.map.rotate(x) self.rotatex += x if y: - self.offsety = 0 + self.offsety = 0.0 for d in self.map: d.rotate(y) self.rotatey += y @@ -169,7 +170,7 @@ class Maze: def slash(self): """Slash the enemies.""" - unit, killist = self.distance/SQRT2 * self.hero.speed, [] + unit, killist = self.distance/SQRT2 * self.hero.spin_speed, [] for i, enemy in enumerate(self.enemies): x, y = enemy.pos(self.distance, self.middlex, self.middley) d = length(x, y, self.x, self.y) @@ -182,33 +183,39 @@ class Maze: for i in reversed(killist): self.enemies.pop(i) self.add_enemy() - def update(self): + def update(self, fps): """Update the maze.""" - modified, d = False, self.distance*1.5 - self.hero.R - self.offsetx += self.right - s = sign(self.offsetx) * 2 - if ((self.map[MIDDLE - s][MIDDLE - 1] - or self.map[MIDDLE - s][MIDDLE] - or self.map[MIDDLE - s][MIDDLE + 1]) - and abs(self.offsetx*self.step) > d): - self.offsetx -= self.right - else: - modified = True + self.offsetx *= fps / self.fps + self.offsety *= fps / self.fps + self.fps, self.speed = fps, fps / MOVE_SPEED + self.step = self.distance / self.speed - self.offsety += self.down - s = sign(self.offsety) * 2 - if ((self.map[MIDDLE - 1][MIDDLE - s] - or self.map[MIDDLE][MIDDLE - s] - or self.map[MIDDLE + 1][MIDDLE - s]) - and abs(self.offsety*self.step) > d): - self.offsety -= self.down - else: - modified = True + modified, d = False, self.distance*1.5 - self.hero.R + if self.right: + self.offsetx += self.right + s = sign(self.offsetx) * 2 + if ((self.map[MIDDLE - s][MIDDLE - 1] + or self.map[MIDDLE - s][MIDDLE] + or self.map[MIDDLE - s][MIDDLE + 1]) + and abs(self.offsetx*self.step) > d): + self.offsetx -= self.right + else: + modified = True + if self.down: + self.offsety += self.down + s = sign(self.offsety) * 2 + if ((self.map[MIDDLE - 1][MIDDLE - s] + or self.map[MIDDLE][MIDDLE - s] + or self.map[MIDDLE + 1][MIDDLE - s]) + and abs(self.offsety*self.step) > d): + self.offsety -= self.down + else: + modified = True if modified: self.map[MIDDLE][MIDDLE] = EMPTY - self.rotate(sign(self.offsetx) * (abs(self.offsetx)==MOVE_SPEED), - sign(self.offsety) * (abs(self.offsety)==MOVE_SPEED)) + self.rotate(sign(self.offsetx) * (abs(self.offsetx)>=self.speed), + sign(self.offsety) * (abs(self.offsety)>=self.speed)) self.map[MIDDLE][MIDDLE] = HERO self.middlex = self.x + self.offsetx*self.step self.middley = self.y + self.offsety*self.step @@ -217,15 +224,15 @@ class Maze: if not enemy.awake: self.wake(enemy) for enemy in self.enemies: - enemy.update(self.distance, self.middlex, self.middley) - self.hero.update() + enemy.update(fps, self.distance, self.middlex, self.middley) + self.hero.update(fps) if self.hero.slashing: self.slash() for enemy in self.enemies: if not enemy.spin_queue: continue x, y = enemy.pos(self.distance, self.middlex, self.middley) d = length(x, y, self.x, self.y) if d <= self.slashd: - self.hero.wound += (self.slashd-d) / self.hero.R / enemy.speed + self.hero.wound += (self.slashd-d) / self.hero.R / enemy.spin_speed pygame.display.flip() pygame.display.set_caption('Brutal Maze - Score: {}'.format( int(self.score - INIT_SCORE))) @@ -238,7 +245,7 @@ class Maze: self.hero.resize() self.distance = (w * h / 416) ** 0.5 - self.step = self.distance / MOVE_SPEED + self.step = self.distance / self.speed self.middlex = self.x + self.offsetx*self.step self.middley = self.y + self.offsety*self.step self.x, self.y = w >> 1, h >> 1 -- cgit 1.4.1