Make recoil independant from FPS and limit shooting speed

This commit is contained in:
Nguyễn Gia Phong 2019-08-25 11:46:17 +07:00
parent c32bc50f58
commit 1ec17fa772
3 changed files with 43 additions and 18 deletions

View File

@ -66,11 +66,16 @@ def neighbors(x, y, z):
for i, j, k in NEIGHBORS: yield x + i*12, y + j*12, z + k*9
def norm(vector):
"""Return the norm of the given vector."""
return numpy.sqrt(numpy.sum(vector**2))
def normalized(*vector):
"""Return normalized vector as a NumPy array of float32."""
v = numpy.float32(vector)
if not any(v): return v
return v / numpy.sqrt(numpy.sum(v**2))
if not v.any(): return v
return v / norm(v)
def sign(x) -> int:

View File

@ -18,13 +18,21 @@
__doc__ = 'Axuy module for character class'
from itertools import combinations
from math import pi, sqrt
from random import random
import numpy as np
from pyrr import matrix33
from .misc import normalized, placeable
from .misc import norm, normalized, placeable
TETRAVERTICES = np.float32([[0, sqrt(8), -1], [sqrt(6), -sqrt(2), -1],
[0, 0, 3], [-sqrt(6), -sqrt(2), -1]]) / 18
OCTOVERTICES = np.float32([(a + b) / 2.0
for a, b in combinations(TETRAVERTICES, 2)])
RPICO = norm(TETRAVERTICES[0])
RSHARD = norm(OCTOVERTICES[0])
INVX = np.float32([[-1, 0, 0], [0, 1, 0], [0, 0, 1]])
INVY = np.float32([[1, 0, 0], [0, -1, 0], [0, 0, 1]])
@ -33,11 +41,12 @@ INVZ = np.float32([[1, 0, 0], [0, 1, 0], [0, 0, -1]])
PICO_SPEED = 1 + sqrt(5) # in unit/s
SHARD_SPEED = PICO_SPEED * 2 # in unit/s
SHARD_LIFE = 3 # bounces
RPS = 6 # rounds per second
class Shard:
"""Fragment broken or shot out of a Picobot, which is a regular
octahedron whose circumscribed sphere's radius is 1/12 unit.
octahedron whose circumscribed sphere's radius is RSHARD.
Parameters
----------
@ -94,7 +103,7 @@ class Shard:
if x is None: x = self.x
if y is None: y = self.y
if z is None: z = self.z
return not placeable(self.space, x, y, z, r=1/12)
return not placeable(self.space, x, y, z, r=RSHARD)
def update(self, fps):
"""Update states."""
@ -119,7 +128,7 @@ class Shard:
class Picobot:
"""Game character, which is represented as a regular tetrahedron
whose circumscribed sphere's radius is 1/4 unit.
whose circumscribed sphere's radius is RPICO.
Parameters
----------
@ -144,6 +153,10 @@ class Picobot:
Rotational matrix.
shards : dict of Shard
Active shards.
recoil_u : np.ndarray of length 3 of np.float32
Recoil direction (unit vector).
recoil_t : float
Recoil time left in seconds.
fps : float
Currently rendered frames per second.
"""
@ -166,6 +179,7 @@ class Picobot:
self.rot = rotation
self.shards = {}
self.recoil_u, self.recoil_t = np.float32([0, 0, 0]), 0.0
self.fps = 60.0
@property
@ -192,7 +206,7 @@ class Picobot:
if x is None: x = self.x
if y is None: y = self.y
if z is None: z = self.z
return placeable(self.space, x, y, z, 1/4)
return placeable(self.space, x, y, z, RPICO)
def rotate(self, yaw, pitch):
"""Rotate yaw radians around y-axis
@ -202,14 +216,25 @@ class Picobot:
@ matrix33.create_from_y_rotation(yaw) @ self.rot)
def move(self, right=0, upward=0, forward=0):
"""Try to move in the given direction."""
"""Try to move in the given direction.
This is the equivalence of Shard.update
and should be called every loop.
"""
dt = 1.0 / self.fps
direction = normalized(right, upward, forward) @ self.rot
x, y, z = self.pos + direction/self.fps*PICO_SPEED
if self.recoil_t:
direction += self.recoil_u * self.recoil_t * RPS
self.recoil_t = max(self.recoil_t - dt, 0.0)
x, y, z = self.pos + direction*dt*PICO_SPEED
if self.placeable(x=x): self.x = x % 12
if self.placeable(y=y): self.y = y % 12
if self.placeable(z=z): self.z = z % 9
def shoot(self):
self.move(forward=-1)
"""Shoot in the forward direction."""
if self.recoil_t: return
self.recoil_t = 1.0 / RPS
self.recoil_u = [0, 0, -1] @ self.rot
self.shards[max(self.shards, default=0) + 1] = Shard(
self.addr, self.space, self.pos, self.rot)

View File

@ -20,9 +20,9 @@ __doc__ = 'Axuy module for map class'
from collections import deque
from configparser import ConfigParser
from itertools import combinations, product
from itertools import product
from os.path import join as pathjoin, pathsep
from math import degrees, log2, radians, sqrt
from math import degrees, log2, radians
from random import randint
from re import IGNORECASE, match
from statistics import mean
@ -36,7 +36,7 @@ from appdirs import AppDirs
from PIL import Image
from pyrr import Matrix44
from .pico import SHARD_LIFE, Picobot
from .pico import TETRAVERTICES, OCTOVERTICES, SHARD_LIFE, Picobot
from .misc import abspath, color, neighbors
CONTROL_ALIASES = (('Move left', 'left'), ('Move right', 'right'),
@ -58,12 +58,7 @@ OYZ = np.float32([[0, 0, 0], [0, 1, 0], [0, 1, 1],
OZX = np.float32([[0, 0, 0], [1, 0, 0], [1, 0, 1],
[1, 0, 1], [0, 0, 1], [0, 0, 0]])
TETRAVERTICES = np.float32([[0, sqrt(8), -1], [sqrt(6), -sqrt(2), -1],
[0, 0, 3], [-sqrt(6), -sqrt(2), -1]]) / 18
TETRAINDECIES = np.int32([0, 1, 2, 3, 1, 2, 0, 3, 2, 0, 3, 1])
OCTOVERTICES = np.float32([(a + b) / 2
for a, b in combinations(TETRAVERTICES, 2)])
OCTOINDECIES = np.int32([0, 1, 2, 0, 1, 3, 4, 0, 2, 4, 0, 3,
2, 1, 5, 3, 1, 5, 2, 5, 4, 3, 5, 4])