Add dynamic framerate
This commit is contained in:
parent
e879dcf58d
commit
bc72fbf25f
|
@ -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)
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue