Math/complex numbers documentation (#313)
* math doc initial commit * PR review * re-add missing images, add new sections, address PR issues * fix typos * PR review and simplification * add readme link * remove unneccessary image
This commit is contained in:
parent
c1b3758388
commit
945a17301f
5 changed files with 181 additions and 3 deletions
176
doc/MATH.rst
Normal file
176
doc/MATH.rst
Normal file
|
@ -0,0 +1,176 @@
|
|||
Complex Numbers in Taisei
|
||||
=========================
|
||||
|
||||
.. contents::
|
||||
|
||||
Introduction
|
||||
''''''''''''
|
||||
|
||||
Taisei uses `complex numbers <https://en.wikipedia.org/wiki/Complex_number>`__
|
||||
in its game code for a number of different functions, such as:
|
||||
|
||||
* Player and enemy position and movement
|
||||
* Danmaku patterns and bullet movement
|
||||
* Special effects and particles
|
||||
* Spell card backgrounds for bosses
|
||||
|
||||
As you can probably see, it's important to have a firm grasp of complex numbers
|
||||
in order to make heads or tails of what the game is doing if you want to
|
||||
develop for it.
|
||||
|
||||
Complex numbers shouldn't be scary. If you have a baseline of middle school
|
||||
mathematics, and certainly if you're familiar with the concept of "variables"
|
||||
in programming, it should only take you a couple of hours to get used to
|
||||
thinking in these terms, and using them effectively in the code.
|
||||
|
||||
Additionally, the C programming language has a very robust support for handling
|
||||
complex numbers, whereas the support for things like vectors and matrices isn't
|
||||
as readily available or pleasant to use.
|
||||
|
||||
There are many different places you can learn about complex numbers, but we've
|
||||
found these two YouTube creators do a better job than we could of explaining
|
||||
the core concepts behind complex numbers.
|
||||
|
||||
* `Imaginary Numbers are Real <https://www.youtube.com/watch?v=T647CGsuOVU&list=PLiaHhY2iBX9g6KIvZ_703G3KJXapKkNaF>`__
|
||||
by Welch Labs - short and punchy video series
|
||||
* `Complex Number Fundamentals <https://www.youtube.com/watch?v=5PcpBw5Hbwo>`__
|
||||
by 3Blue1Brown - a longer lecture-style format
|
||||
|
||||
More helpful links:
|
||||
|
||||
* `Formula for converting a *cartesian coordinate* to a *polar coordinate* <https://www.engineeringtoolbox.com/converting-cartesian-polar-coordinates-d_1347.html>`__
|
||||
|
||||
Core Concept
|
||||
''''''''''''
|
||||
|
||||
One important fact about complex numbers is that one can think of the same
|
||||
complex number in two ways: either as a traditional two-dimensional vector or
|
||||
as a length and an angle (measured starting from the positive X-axis). When
|
||||
adding two complex numbers, they behave exactly like vectors.
|
||||
|
||||
But in contrast to vectors, one can also multiply them together and get a new
|
||||
complex number. In this multiplication, their second interpretation becomes
|
||||
useful: multiplying two complex numbers means multiplying their lengths and
|
||||
adding their angles.
|
||||
|
||||
In-Game Examples
|
||||
''''''''''''''''
|
||||
|
||||
Coordinate System
|
||||
^^^^^^^^^^^^^^^^^
|
||||
|
||||
Taisei's gameplay takes place entirely on a 2D plane, using an "X" and "Y"
|
||||
coordinate system.
|
||||
|
||||
.. image:: images/taisei-xy-01.png
|
||||
:width: 300pt
|
||||
|
||||
Complex numbers in Taisei represent the "X-axis" (or "real") and "Y-axis" (or
|
||||
"imaginary"). This is not exactly a widely-adopted way of thinking about it,
|
||||
but as mentioned in *Core Concept*, it allows us to perform operations on the
|
||||
coordinate system that wouldn't otherwise be possible using vectors. There are
|
||||
pros and cons to either way of doing it, with complex numbers being more
|
||||
involved to use but providing a few worthwhile advantages.
|
||||
|
||||
Simple Movement
|
||||
^^^^^^^^^^^^^^^
|
||||
|
||||
Let's take a few concrete examples within Taisei's code. For let's look at a
|
||||
piece of movement code for a fairy:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
enemy->move = move_towards(VIEWPORT_W/2.0 + 200.0 * I, 0.035);
|
||||
|
||||
The important piece here, for our purposes, is:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
VIEWPORT_W/2.0 + 200.0 * I
|
||||
|
||||
What this is doing is specifying a position on the "real" X-axis
|
||||
(``VIEWPORT_W/2.0``, which in Taisei means ``480.0 / 2.0`` or `240.0`), and
|
||||
then specifying a position on the "imaginary" Y-axis (``200.0 * I``). The ``I``
|
||||
here is the same ``i`` described in the videos and the above explanation.
|
||||
|
||||
So what we're really looking at here is:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
240.0 (real) + 200.0i (imaginary)
|
||||
|
||||
Or "move 240 units on the (real) X-axis, and then 200 units on the (imaginary)
|
||||
Y-axis."
|
||||
|
||||
This is what's called a **Cartesian Coordinate.** What the function
|
||||
``move_towards`` then does is make the enemy sprite/object move towards that
|
||||
point on the X/Y axis at a certain rate (defined by ``0.035``).
|
||||
|
||||
Simple Danmaku
|
||||
^^^^^^^^^^^^^^
|
||||
|
||||
Let's look at a danmaku pattern to see how complex numbers are used in-game.
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
cmplx aim = cnormalize(global.plr.pos - enemy->pos);
|
||||
|
||||
This ``aim`` variable could be passed to a ``move_towards`` function attached
|
||||
to a ``PROJECTILE`` object. The effect is bullets shooting directly at the
|
||||
player in a straight line, wherever on the screen they may be at the time.
|
||||
|
||||
Let's look at the argument inside ``cnormalize`` first, ``global.plr.pos -
|
||||
e->pos``. Both ``global.plr.pos`` and ``e->pos`` are *complex numbers*, in
|
||||
that they have both *real* and *imaginary* parts. Much like the example in
|
||||
``Simple Movement``, they represent a place on the X/Y grid.
|
||||
|
||||
In the format of ``[X,Y]``, let's say that ``global.plr.pos`` is ``[-1, 6]``,
|
||||
and that ``enemy->pos`` is ``[6, 3]``.
|
||||
|
||||
.. image:: images/math-01.png
|
||||
:width: 300pt
|
||||
|
||||
When you subtract ``[6, 3]`` (enemy position) from ``[-1, 6]`` (player
|
||||
position), you end up with ``[-7, 3]``, as seen here with ``plr->pos``.
|
||||
|
||||
.. image:: images/math-02.png
|
||||
:width: 300pt
|
||||
|
||||
This also conveniently lets the enemy position ``enemy->pos`` become the new
|
||||
"origin," or ``[0, 0]``. This is useful because it means that we can more
|
||||
easily determine what angle the danmaku need to travel in to travel towards the
|
||||
player.
|
||||
|
||||
As a vector, ``[-7, 3]`` points from the enemy position to the player position. Its
|
||||
length is the distance between enemy and player. Its angle is the direction
|
||||
we want the danmaku to travel in. In this example, we don’t care about the
|
||||
distance. We want a unit length pointer towards the player. ``cnormalize()`` does
|
||||
this for us by giving us a complex number with the same angle as its argument
|
||||
but with a length of "1".
|
||||
|
||||
Let's consider how we might use this new ``aim`` variable later on, say in a
|
||||
``PROJECTILE`` block for a danmaku bullet:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
// aim directly at the player
|
||||
cmplx aim = cnormalize(global.plr.pos - enemy->pos);
|
||||
|
||||
// a bit of randomization
|
||||
cmplx offset = cdir(M_PI/180 * rng_sreal());
|
||||
|
||||
// later, inside a PROJECTILE() block...
|
||||
.move = move_asymptotic_simple(aim * offset, 5),
|
||||
|
||||
The important piece here is the ``aim * offset`` inside the ``move()`` block.
|
||||
Being able to multiply complex numbers by each other means "procedurally"
|
||||
generating danmaku patterns becomes much easier. Multiplying two complex
|
||||
numbers together like this means adding their angles, and in the case of
|
||||
something like ``cdir(M_PI/180 * rng_sreal())``, you can quickly do rotations in
|
||||
your patterns without handling cumbersome matrices. In this case, we add some
|
||||
random scattering to the original direction of "shoot directly at the player"
|
||||
contained in ``aim`` with an additional ``offset`` angle.
|
||||
|
||||
Hopefully, you can see now why complex numbers provides several advantages with
|
||||
the slight trade-off of being slightly more esoteric in the context of game
|
||||
programming.
|
|
@ -16,11 +16,13 @@ Development
|
|||
and compile Taisei
|
||||
* `ENVIRON.rst <./ENVIRON.rst>`__ - various environment variables built
|
||||
into Taisei to modify its core settings, when run from the command line
|
||||
* `MATH.rst <./MATH.rst>`__ - introduction to complex numbers, used in the
|
||||
coordinate system of Taisei
|
||||
|
||||
Graphics
|
||||
--------
|
||||
|
||||
* `ANIMATION_FORMAT.rst <./ANIMATION_FORMAT.rst>`__ - how to produce sprite animations within the
|
||||
* `ANIMATION_FORMAT.rst <./ANIMATION_FORMAT.rst>`__ - how to produce sprite
|
||||
animations within the game engine
|
||||
* `BASISU.rst <./BASISU.rst>`__ - how to compress certain assets within the
|
||||
game engine
|
||||
* `BASISU.rst <./BASISU.rst>`__ - how to compress certain assets within the game
|
||||
engine
|
||||
|
|
BIN
doc/images/math-01.png
Normal file
BIN
doc/images/math-01.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 200 KiB |
BIN
doc/images/math-02.png
Normal file
BIN
doc/images/math-02.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 205 KiB |
BIN
doc/images/taisei-xy-01.png
Normal file
BIN
doc/images/taisei-xy-01.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 376 KiB |
Loading…
Reference in a new issue