Shortest angle from ‘current’ to ‘desired’ angle

6 08 2008

I figured someone might Google this one one day:

While doing some 3d programming in Python, for FiniteDreams, I was trying to rotate an object from whichever direction that it is currently facing, toward a new desired direction.  I’m only really concerned with “heading“, or “yaw“, which does the side-to-side sweeping in a Z-up coordinate system.

I have a velocity vector built each frame, influenced by whichever combination of directional movement keys are being held.  In order to get my character (or any object with such velocity) to gradually point in the right direction, I had to convert the directional velocity vector into some “heading” value:

>>> from math import atan2
>>> newHeadingAngle = atan2(newVelocityVector.getY(),newVelocityVector.getX()) * (180/pi)

As you can see, atan2 must be imported from the math library.

Next, I have to find the shortest angle between where my current angle is, and where I want to go.  There’s  no sense making my character turn 300 degrees clockwise if he only has to move 60 degrees counter-clockwise:

>>> shortestAngle = newHeadingAngle - currentAngle
>>> if shortestAngle > 180:
...    shortestAngle -= 360
>>> if shortestAngle < -180:
...    shortestAngle += 360

And voila.  Now you’ve got the angle in the range [-180,180], acording to the direction that the object needs to rotate to get to it’s desired heading.

But, we don’t just want to instantly make the character face that direction.  Instead, s/he needs to gradually turn there.  The advantage that we now have is that we have already calculated an angle that needs to be traveled.  All that remains to do is move the character toward it each frame:

>>> movementThisFrame = self.shortestAngle * characterAttribute_speed * elapsedTime
>>> self.shortestAngle -= movementThisFrame
>>> characterNodePath.setH(characterNodePath.getH() + movementThisFrame)

Note that I’ve used the “self” syntax this time around, since I’m getting more specific to the panda3d code.  The “self” variables are persistent in a class object; the context of this code is a panda3d “task“, which is a method that gets executed each frame.  Thus, I need to retain certain of my calculations.  In this case, I need to remember that “shortestAngle” variable, so that each frame can move closer and closer towards it.

The first line takes the character’s speed into account, so that I can have a big fat character turn more slowly than a thin one.  The “elapsedTime” variable is the total time elapsed since the end of the last frame (which I’ve calculated myself).  This regulates the speed at which things happen.  If I do not include this variable, the game would run at terrifyingly unpredictable speeds on different computers, depending on the hardware.

Line 2 subtracts the distance that we want to move this frame, from  total angle that we would like to move towards.

Line 3 actually performs the rotation.  This line does not make a smooth movement out of my new angle, which is why I’m calculating a “movementThisFrame” variable, which is quite small compared to the total angle.  Since it is small, line 3 doesn’t have the visual effect of being choppy or chunky.

Also important is to remember to reduce down the degree of rotation that the character is currently in.  Even though the target angle is reduced, adding an angle of 178 degrees to an existing angle of 164 will add up to 342 degrees of rotation.  The model will still be pointing the right direction, but to stop from winding dozens of times into higher and higher angle numbers, this should be reduced via the same angle-checking method used above, for the “shortestAngle” snippet, two code blocks above.  The only difference would be to use “characterNodePath.setH(characterNodePath.getH() +/- 360)”  If done every frame, the character will always appear to move and point in the right direction.

As one last note, I’ve made a “if” test on the remaining “self.shortestAngle” variable, so that if the remaining angle distance to be covered is less than 0.05 then I just zero it out.

Python basics

5 08 2008

Python‘s great, really. It takes a little bit of a mode-change before you get used to it, but I’m really quite taken with it. I’ve got experience with C/++, Java, Perl, PHP, Javascript, and a little in Ruby (and I can hammer things out in a number of others, but I wouldn’t claim to “know” the language), and I’m simply impressed with Python.One major plus: Python was built to be a command-line tool.  Thus, you can “run” Python and it will be in command-line form, where you can enter one line at a time.  Sounds rather daft until you’re sitting on a hard chair at 3am trying to guess at how a method behaves.  Python makes this sort of guesswork trivial, as you can just open the command prompt (or terminal, if you’re on some Unix-based system) and run “python”.  You have the ability to simply test a command several times over, to observe its behavior.This INVALUABLE to a person like myself.  There are times when (let’s face it), the online documentation is a bunch of crap, or (in my case), the documentation on the panda3d library’s methods is incomplete.  You have the power to just *do* the command and see for yourself, rather than trying to debug your current program for strange behavior, or trying to built a seperate program for the sole purpose of testing a function or method.  I’ll be honest, I usually have a Python process running in my command prompt while I program in my IDE off on the other monitor.

Some quick observations about Python: 

  • Global scoping can be weird.  Try to be as encapsulated as possible.  Python’s scoping is more like “static” (if you’re familiar with the C++ idea of that term).  There’s no cross-file variable/function/method sharing.
  • Functions” are created with the “def” keyword.
  • Any functions (“def”s) that are within a Class always take “self” as their first arguement, by convention.  When you call these functions/methods, they do NOT require that you pass an arguement to fill the “self” variable in your declaration’s arguement list.  This is resolved automatically.  It is like the “this” pointer of C++.  It’s a reference to the class object that the “def” belongs to.
  • Any variables that should be persistently available in a class should be accessed by the “self.myVar” syntax.
  • A method defined by “def __init__(self):” will act as the class’s constructor.
  • Triple-quoted strings (using three single quote or double quote characters in a row) can be used as something called a “docstring“, and should be placed directly under each class and def statement’s first line.  Triple-quoted strings are really just immune to linebreaks.  That’s all that’s special about  them.  They just happen to be used for this “docstring” very commonly.
  • There are no braces in Python code. Indentation is the way that Python knows what block you’re in.  All statements that should be within an “if” block should be indented the same amount–no more, no less.
  • General lines do not require a semicolon at the end. This is because Python was made for line-by-line console programming.  Making every line end with a “;” is a silly notion.  Just push “enter” on the prompt to finish the line.  However, including a “;” at the end allows you to execute multiple statements on a single line–functionality only really useful from the command prompt.
  • Blocked statements, like “if”, “else”, “elif”, “while”, “for”, “def”, “class”, etc, require a colon at the end. This seems utterly backwards to the normal programming standards out there, but again, on the console, it makes sense; this is how Python knows that you want to enter more information to be within the block that you’ve just tried to define.
  • Everything in Python is by reference. Sounds annoying at first, but it makes the programs a bit more lightweight on the memory footprint.  Variable assignment, if dealing with Objects, is by reference:
    >>> a = myClassObject()
    >>> b = a
    >>> b.setX(3)
    >>> a.getX()
  • Comments are made with a pound, or hash, sign “#”.  There are no multi-line comments.  You could, however, be a jacker and abuse the triple-quote string to get multi-line “comments” in your code.  It’s nicer just to put a “#” at the front of each line.  C’mon… how lazy ARE you?

And those are some of the basics.

Finite Dreams 0.1.23

5 08 2008

I’ve recently taken on some 3d programming with a set of libraries called “panda3d“.  They’re quite nice, and I’ve been impressed with it all so far.  The main interfacing language is Python, although many of the libraries themselves are written in C++, for speed’s sake.

Ever since (about) highschool, I had this vision of creating my own RPG.  Originally, my naive idea was to make a sequel to the beloved SNES game Chrono Trigger, but then it hit me one that day that…

A) I’d be an idiot for trying to make a “sequel” to a copyrighted video game produced by a largely successful company based out of Japan

B) I felt silly for trying to be “new” and “on the edge of discovery” while only (at best) attempting to extend the ideas of a genius other than I.

C) You don’t just “make a sequel” to your favorite video game.  That’s childish.

While all of the above sound pretty similar in nature, all three hit me differently, and I ultimately decided to completely redo the “story” that I had invented.  I had been drawing characters for use in the game for a long time, yet I still decided that even those needed a make over.

And over the next 3 years, the idea for Finite Dreams (FD) had been in the making.  (The name is kind of tentative, since I came up with it a while back, and I don’t think that I like it much anymore.)  Games from all over the spectrum (a few of the more relevant examples: Kingdom Hearts, Dark Cloud 2, and Final Fantasy XII) had developed ideas at the same time as myself, and I constantly found myself playing a brand new game that had come up with my ideas before I had.  *grr*

But now, I’m creating “my” game.  I’m not recreating someone else’s ideas, or implamenting them differently, or even doing them “better” than in some other game.  I’m making my own ideas, and it’s coming along nicely…  Unfortunately for me, I had to begin learning Python, but now I love it.  It makes me *hate* Java all the more.  *shudder*  ooooOoooOOOo, Java, how I loathe you.  But the guys at work hear me rant enough about Java, so I shan’t do it here.

I’ll be sure to post some actual content eventually.  For now, I’m getting the engine up and running.  While I’m a one-man team, I do hope to make some good progress, even if it takes me ages.  I’m an artist, muscian, programmer, modeler, and storywriter, all wrapped up into one.  I love every single one of those elements.  I can’t wait to hit every milestone along the way.