Improve drawing system

This commit is contained in:
Nguyễn Gia Phong 2017-10-19 20:28:56 +07:00
parent bc72fbf25f
commit 43c7c55a39
5 changed files with 102 additions and 72 deletions

View File

@ -20,44 +20,13 @@
__doc__ = 'brutalmaze module for hero and enemy classes' __doc__ = 'brutalmaze module for hero and enemy classes'
from collections import deque from collections import deque
from math import atan2, cos, sin, pi from math import atan2, sin, pi
from random import shuffle from random import shuffle
import pygame import pygame
from pygame.gfxdraw import filled_polygon, aapolygon
from .constants import * from .constants import *
from .utils import randsign, regpoly, fill_aapolygon, pos, sign
def randsign():
"""Return either -1 or 1 (kind of) randomly."""
return (pygame.time.get_ticks() & 1)*2 - 1
def regpoly(n, R, r, x, y):
"""Return the pointlist of the regular polygon with n sides,
circumradius of R, the center point I(x, y) and one point A make the
vector IA with angle r (in radians).
"""
r %= pi * 2
angles = [r + pi*2*side/n for side in range(n)]
return [(x + R*cos(angle), y + R*sin(angle)) for angle in angles]
def fill_aapolygon(surface, points, color):
"""Draw a filled polygon with anti aliased edges onto a surface."""
aapolygon(surface, points, color)
filled_polygon(surface, points, color)
def pos(x, y, distance, middlex, middley):
"""Return coordinate of the center of the grid (x, y)."""
return middlex + (x - MIDDLE)*distance, middley + (y - MIDDLE)*distance
def sign(n):
"""Return the sign of number n."""
return -1 if n < 0 else 1 if n else 0
class Hero: class Hero:
@ -85,20 +54,19 @@ class Hero:
if hold: self.spin_queue.extend([0] * (self.spin_speed >> 1)) if hold: self.spin_queue.extend([0] * (self.spin_speed >> 1))
self.spin_queue.extend([randsign()] * self.spin_speed) self.spin_queue.extend([randsign()] * self.spin_speed)
def draw(self, color=None): def draw(self):
"""Draw the hero.""" """Draw the hero."""
trigon = regpoly(3, self.R, self.angle, self.x, self.y) trigon = regpoly(3, self.R, self.angle, self.x, self.y)
fill_aapolygon(self.surface, trigon, color or self.get_color()) fill_aapolygon(self.surface, trigon, self.get_color())
def update(self, fps): def update(self, fps):
"""Update the hero.""" """Update the hero."""
self.spin_speed = int(round(fps / (len(self.color)-self.wound))) self.spin_speed = int(round(fps / (len(self.color)-self.wound)))
self.wound -= HEAL_SPEED / len(self.color) / (self.spin_speed or 1) self.wound -= HEAL_SPEED / len(self.color) / self.spin_speed
if self.wound < 0: self.wound = 0.0 if self.wound < 0: self.wound = 0.0
self.slash(hold=True) self.slash(hold=True)
direction = self.spin_queue.popleft() if self.spin_queue else 0 direction = self.spin_queue.popleft() if self.spin_queue else 0
self.draw(color=BG_COLOR)
if direction: if direction:
self.angle += direction * pi * 2 / 3 / self.spin_speed self.angle += direction * pi * 2 / 3 / self.spin_speed
else: else:
@ -125,9 +93,8 @@ class Enemy:
self.awake = False self.awake = False
self.move_speed = fps / MOVE_SPEED self.move_speed = fps / MOVE_SPEED
self.offsetx = self.offsety = 0 self.offsetx = self.offsety = 0
self.spin_speed = int(round(fps / len(self.color))) self.spin_speed = fps / len(self.color)
self.spin_queue = [] self.spin_queue = self.wound = 0.0
self.wound = 0.0
def pos(self, distance, middlex, middley): def pos(self, distance, middlex, middley):
"""Return coordinate of the center of the enemy.""" """Return coordinate of the center of the enemy."""
@ -135,11 +102,12 @@ class Enemy:
step = distance / self.move_speed step = distance / self.move_speed
return x + self.offsetx*step, y + self.offsety*step return x + self.offsetx*step, y + self.offsety*step
def draw(self, distance, middlex, middley, color): def draw(self, distance, middlex, middley):
"""Draw the enemy, given distance between grids and the middle grid.""" """Draw the enemy, given distance between grids and the middle grid."""
radious = distance/SQRT2 - self.awake*2 radious = distance/SQRT2 - self.awake*2
square = regpoly(4, radious, self.angle, square = regpoly(4, radious, self.angle,
*self.pos(distance, middlex, middley)) *self.pos(distance, middlex, middley))
color = self.color[int(self.wound)] if self.awake else FG_COLOR
fill_aapolygon(self.surface, square, color) fill_aapolygon(self.surface, square, color)
def place(self, x=0, y=0): def place(self, x=0, y=0):
@ -177,14 +145,16 @@ class Enemy:
def update(self, fps, distance, middlex, middley): def update(self, fps, distance, middlex, middley):
"""Update the enemy.""" """Update the enemy."""
if self.awake: if self.awake:
self.draw(distance, middlex, middley, BG_COLOR) self.spin_speed, old_speed = fps / len(self.color), self.spin_speed
self.spin_queue *= self.spin_speed / old_speed
if not self.spin_queue and not self.move(fps): if not self.spin_queue and not self.move(fps):
self.spin_speed = int(round(fps / len(self.color))) self.spin_queue = randsign() * self.spin_speed
self.spin_queue.extend([randsign()] * self.spin_speed) if abs(self.spin_queue) > 0.5:
if self.spin_queue: self.angle += sign(self.spin_queue) * pi / 2 / self.spin_speed
self.angle += self.spin_queue.pop() * pi / 2 / self.spin_speed self.spin_queue -= sign(self.spin_queue)
self.draw(distance, middlex, middley, else:
self.color[int(self.wound)] if self.awake else FG_COLOR) self.angle, self.spin_queue = pi / 4, 0
self.draw(distance, middlex, middley)
def die(self): def die(self):
"""Kill the enemy.""" """Kill the enemy."""

View File

@ -30,7 +30,7 @@ GOLDEN_MEAN = 5**0.5/2 + 0.5
INIT_FPS = 30.0 INIT_FPS = 30.0
SIZE = 640, 480 SIZE = 640, 480
MAZE_SIZE = 12 MAZE_SIZE = 10
ROAD_WIDTH = 5 ROAD_WIDTH = 5
CELL_WIDTH = ROAD_WIDTH * 2 CELL_WIDTH = ROAD_WIDTH * 2
MIDDLE = (MAZE_SIZE + MAZE_SIZE%2 - 1)*ROAD_WIDTH + ROAD_WIDTH//2 MIDDLE = (MAZE_SIZE + MAZE_SIZE%2 - 1)*ROAD_WIDTH + ROAD_WIDTH//2

View File

@ -69,8 +69,8 @@ def main():
maze.hero.slashing = False maze.hero.slashing = False
elif event.type == VIDEORESIZE: elif event.type == VIDEORESIZE:
maze.resize(event.w, event.h) maze.resize(event.w, event.h)
if len(flash_time) > 10: if len(flash_time) > 5:
new_fps = 10000.0 / (flash_time[-1] - flash_time[0]) new_fps = 5000.0 / (flash_time[-1] - flash_time[0])
fps += -1 if new_fps < fps else 5 fps += -1 if new_fps < fps else 5
flash_time.popleft() flash_time.popleft()
maze.update(fps) maze.update(fps)

View File

@ -18,29 +18,18 @@
# Copyright (C) 2017 Nguyễn Gia Phong # Copyright (C) 2017 Nguyễn Gia Phong
from collections import deque from collections import deque
from math import pi, atan, cos, sin, log from math import pi, atan, log
from random import choice, getrandbits from random import choice, getrandbits
import pygame import pygame
from .characters import pos, sign, regpoly, fill_aapolygon, Hero, Enemy from .characters import Hero, Enemy
from .constants import * from .constants import *
from .utils import pos, sign, cosin, length, regpoly, fill_aapolygon
__doc__ = 'brutalmaze module for the maze class' __doc__ = 'brutalmaze module for the maze class'
def cosin(x):
"""Return the sum of cosine and sine of x (measured in radians)."""
return cos(x) + sin(x)
def length(x0, y0, x1, y1):
"""Return the length of the line segment joining the two points
(x0, y0) and (x1, y1).
"""
return ((x0-x1)**2 + (y0-y1)**2)**0.5
def cell(bit, upper=True): def cell(bit, upper=True):
"""Return a half of a cell of the maze based on the given bit.""" """Return a half of a cell of the maze based on the given bit."""
if bit: return deque([WALL]*ROAD_WIDTH + [EMPTY]*ROAD_WIDTH) if bit: return deque([WALL]*ROAD_WIDTH + [EMPTY]*ROAD_WIDTH)
@ -70,7 +59,7 @@ class Maze:
self.distance = (self.w * self.h / 416) ** 0.5 self.distance = (self.w * self.h / 416) ** 0.5
self.step = self.distance / self.speed self.step = self.distance / self.speed
self.middlex, self.middley = self.x, self.y = self.w >> 1, self.h >> 1 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) w, h = (int(i/self.distance/2 + 2) for i in size)
self.rangex = range(MIDDLE - w, MIDDLE + w + 1) self.rangex = range(MIDDLE - w, MIDDLE + w + 1)
self.rangey = range(MIDDLE - h, MIDDLE + h + 1) self.rangey = range(MIDDLE - h, MIDDLE + h + 1)
self.offsetx = self.offsety = 0.0 self.offsetx = self.offsety = 0.0
@ -219,10 +208,10 @@ class Maze:
self.map[MIDDLE][MIDDLE] = HERO self.map[MIDDLE][MIDDLE] = HERO
self.middlex = self.x + self.offsetx*self.step self.middlex = self.x + self.offsetx*self.step
self.middley = self.y + self.offsety*self.step self.middley = self.y + self.offsety*self.step
self.draw()
for enemy in self.enemies: for enemy in self.enemies:
if not enemy.awake: self.wake(enemy) if not enemy.awake: self.wake(enemy)
self.draw()
for enemy in self.enemies: for enemy in self.enemies:
enemy.update(fps, self.distance, self.middlex, self.middley) enemy.update(fps, self.distance, self.middlex, self.middley)
self.hero.update(fps) self.hero.update(fps)
@ -230,9 +219,9 @@ class Maze:
for enemy in self.enemies: for enemy in self.enemies:
if not enemy.spin_queue: continue if not enemy.spin_queue: continue
x, y = enemy.pos(self.distance, self.middlex, self.middley) x, y = enemy.pos(self.distance, self.middlex, self.middley)
d = length(x, y, self.x, self.y) d = self.slashd - length(x, y, self.x, self.y)
if d <= self.slashd: if d >= 0:
self.hero.wound += (self.slashd-d) / self.hero.R / enemy.spin_speed self.hero.wound += d / self.hero.R / enemy.spin_speed
pygame.display.flip() pygame.display.flip()
pygame.display.set_caption('Brutal Maze - Score: {}'.format( pygame.display.set_caption('Brutal Maze - Score: {}'.format(
int(self.score - INIT_SCORE))) int(self.score - INIT_SCORE)))
@ -249,9 +238,10 @@ class Maze:
self.middlex = self.x + self.offsetx*self.step self.middlex = self.x + self.offsetx*self.step
self.middley = self.y + self.offsety*self.step self.middley = self.y + self.offsety*self.step
self.x, self.y = w >> 1, h >> 1 self.x, self.y = w >> 1, h >> 1
w, h = int((w/self.distance+2) / 2), int((h/self.distance+2) / 2) w, h = int(w/self.distance/2 + 2), int(h/self.distance/2 + 2)
self.rangex = range(MIDDLE - w, MIDDLE + w + 1) self.rangex = range(MIDDLE - w, MIDDLE + w + 1)
self.rangey = range(MIDDLE - h, MIDDLE + h + 1) self.rangey = range(MIDDLE - h, MIDDLE + h + 1)
self.slashd = self.hero.R + self.distance/SQRT2
self.draw() self.draw()
def move(self, x, y): def move(self, x, y):
@ -265,4 +255,4 @@ class Maze:
def lose(self): def lose(self):
"""Handle loses.""" """Handle loses."""
quit() pygame.quit()

70
brutalmaze/utils.py Normal file
View File

@ -0,0 +1,70 @@
# -*- coding: utf-8 -*-
# characters.py - module for shared functions and macros
# This file is part of brutalmaze
#
# brutalmaze is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# brutalmaze is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with brutalmaze. If not, see <http://www.gnu.org/licenses/>.
#
# Copyright (C) 2017 Nguyễn Gia Phong
__doc__ = 'brutalmaze module for hero and enemy classes'
from math import cos, sin, pi
import pygame
from pygame.gfxdraw import filled_polygon, aapolygon
from .constants import *
def randsign():
"""Return either -1 or 1 (kind of) randomly."""
return (pygame.time.get_ticks() & 1)*2 - 1
def regpoly(n, R, r, x, y):
"""Return the pointlist of the regular polygon with n sides,
circumradius of R, the center point I(x, y) and one point A make the
vector IA with angle r (in radians).
"""
r %= pi * 2
angles = [r + pi*2*side/n for side in range(n)]
return [(x + R*cos(angle), y + R*sin(angle)) for angle in angles]
def fill_aapolygon(surface, points, color):
"""Draw a filled polygon with anti aliased edges onto a surface."""
aapolygon(surface, points, color)
filled_polygon(surface, points, color)
def pos(x, y, distance, middlex, middley):
"""Return coordinate of the center of the grid (x, y)."""
return middlex + (x - MIDDLE)*distance, middley + (y - MIDDLE)*distance
def sign(n):
"""Return the sign of number n."""
return -1 if n < 0 else 1 if n else 0
def cosin(x):
"""Return the sum of cosine and sine of x (measured in radians)."""
return cos(x) + sin(x)
def length(x0, y0, x1, y1):
"""Return the length of the line segment joining the two points
(x0, y0) and (x1, y1).
"""
return ((x0-x1)**2 + (y0-y1)**2)**0.5