tag:blogger.com,1999:blog-46571420001833637952024-02-06T20:07:48.526-08:00Base Metal GamesIndie Game Dev LogAnonymoushttp://www.blogger.com/profile/04441790807840642185noreply@blogger.comBlogger94125tag:blogger.com,1999:blog-4657142000183363795.post-61861552337667479422015-06-08T17:00:00.000-07:002015-06-08T17:00:04.859-07:00Sprink!Sprink has been released for iOS and PC!<br />
<br />
<a href="http://www.basemetalgames.com/sprink.html">http://www.basemetalgames.com/sprink.html</a><br />
<br />
<blockquote class="tr_bq">
As spring awakens from a long winter's rest, Sprink awakes and begins a
journey to find a sunflower! Ascend skyward through the seasons, to the
stars!<br /><br />Sprink is a heartwarming, family-friendly platform
jumping game about rebirth, working towards your goals, and picking
yourself up again when you fall - with a little help from your friends.
Enjoy!</blockquote>
<br />
In April, I did a month-long game jam on a forum of a friend of mine (<a href="http://www.supershigi.com/forums/">http://www.supershigi.com/forums/</a> - Laura Shigihara, composer for Plants vs. Zombies, game developer and all-around awesome person). The theme for the game jam was "Spring" - so, of course, the game starts with the story of a spring awakening in the spring.<br />
<br />
I had a lot of fun making this game. It was difficult to get everything scheduled and done in one month, as I not only had to code the game, but also do all of the artwork, music, menus, and probably a dozen other things I have forgotten about.<br />
<br />
The last month has been spent fixing up things for iOS, and waiting for approval on the App Store. <br />
<br />
Overall, it went quite smoothly. The game is fairly short, but there is a lot of heart put into it, and you can replay it a lot to try to beat your high score. :)<br />
<br />
Enjoy!<br />
<br />Anonymoushttp://www.blogger.com/profile/04441790807840642185noreply@blogger.com1tag:blogger.com,1999:blog-4657142000183363795.post-49998198285881138362015-01-22T09:00:00.000-08:002015-01-22T09:00:03.389-08:00Interpolation<a href="http://en.wikipedia.org/wiki/Interpolation">Interpolation</a> is basically just the creation of intermediate points between two points. In games, this is quite often used to move an object smoothly from one place to another over several frames.<br />
<br />
<h4>
Linear Interpolation </h4>
<br />
The basic form of interpolation is linear interpolation (lerp). This basically looks like:<br />
<br />
p = p0*(1 - t) + p1*t<br />
<br />
where:<br />
p0 is the start value<br />
p1 is the end value<br />
t is a value between 0 and 1 representing the interpolation amount<br />
p is the current value<br />
<br />
This basically represents a straight line segment between <i>p0 </i>and <i>p1, </i>with <i>t </i>representing how far along the line we are.<br />
<br />
An equivalent representation is:<br />
p = p0 + (p1 - p0)*t<br />
<br />
This form does have some possible performance advantages. First, there is only one multiplication to perform. Second, since <i>p0 </i>and <i>p1 </i>are constants, it is also possible to pre-compute the subtraction.<br />
<br />
This basic formula also works for vectors, so <i>p0 </i>and <i>p1 </i>can be single floating point values, 2D or 3D points, or vectors of any other size.<br />
<br />
This is such a common and basic function that it is an intrinsic function in <a href="http://msdn.microsoft.com/en-us/library/windows/desktop/bb509618%28v=vs.85%29.aspx">shader</a> <a href="http://http.developer.nvidia.com/Cg/lerp.html">languages</a>.<br />
<br />
<h4>
Normalization</h4>
<br />
Quite often you will find that the values you want to use as the interpolation amount (<i>t</i>) are not between 0 and 1.<br />
<br />
If you have a low and high range, it's fairly straightforward to normalize the values to a range between 0 and 1:<br />
<br />
t = clamp((t0 - x0) / (x1 - x0))<br />
where:<br />
x0 is the minimum range value<br />
x1 is the maximum range value<br />
t0 is the current value<br />
clamp is a function that forces values less than 0 to be come 0, and greater than 1 to become 1<br />
t is the normalized value<br />
<br />
<h4>
Smooth Interpolation</h4>
<br />
Suppose we have three points, <i>p0, p1, </i>and <i>p2</i>. We can interpolate between <i>p0 </i>and <i>p1</i>, and then from <i>p1 </i>to <i>p2. </i>This allows us to follow a path through multiple points, but the results are usually not very satisfactory. For motion of an object, changes in speed and direction are very abrupt and unnatural. <br />
<br />
One easy way to smooth out these transitions is to modify the value of <i>t </i>before doing the interpolation. The main advantage to this is the ability to reuse the basic linear interpolation function for various non-linear interpolation methods.<br />
<br />
<a href="http://en.wikipedia.org/wiki/Smoothstep">Smoothstep</a> is a commonly used function for this purpose. The smoothstep function, with its first and second derivatives:<br />
t = 3*t0^2 - 2*t0^3<br />
t' = 6*t0 - 6*t0^2<br />
t'' = 6 - 12*t0<br />
where:<br />
t0 is the initial value<br />
t is the new value<br />
t' and t'' are the first and second derivatives<br />
<br />
Because <i>t' </i>(the first derivative) is zero at <i>t0=0 </i>and <i>t0=1, </i>there will be less apparent discontinuity as we pass through each point.<br />
<br />
The second derivative is still non-zero at <i>t0=1</i>, which may still be perceived as a discontinuity. In terms of physics, this would be the same as having zero velocity at the end point, but non-zero acceleration.<br />
<br />
This function is common enough to be, like lerp, implemented in <a href="http://msdn.microsoft.com/en-us/library/windows/desktop/bb509658%28v=vs.85%29.aspx">shader</a> <a href="http://http.developer.nvidia.com/Cg/smoothstep.html">languages</a>. Implementations for smoothstep typically also include the normalization step, so t = smoothstep(0, 1, t0) is equivalent to the basic smoothstep function.<br />
<br />
A smoother possible function is:<br />
<br />
t = 6*t0^5 - 15*t0^4+10*t0^3<br />
t' = 30*t0^4 - 60*t0^3 + 30*t0^2<br />
t'' = 120*t0^3 - 180*t0^2 + 60*t0<br />
<br />
This will have first and second derivatives equal to zero at <i>t0=0 </i>and <i>t0=1.</i><br />
<br />
<br />
<h4>
Other Curves</h4>
<br />
<br />
There are many other possible functions that you may find useful, depending on the circumstances. In general, any function that gives output values in the range [0,1] for input in the range [0,1] will work.<br />
<br />
t = t0^2<br />
Fast End - starts slowly at <i>t=0</i>, but increases rapidly towards <i>t=1</i>:<br />
<br />
t = t0*(2-t0) = 1-(1-t0)*(1-t0)<br />
Fast Start - essentially the inverse of Fast End<br />
<br />
t = 2*t0^3 - 3*t0^2 + 2*t0<br />
Fast Start End - fast start, slow middle and fast end values<br />
<br />
t = 0.5 - cos(PI*t0)*0.5<br />
Cosine interpolation<br />
<br />
<h4>
Conclusion</h4>
<br />
I have found it useful to have all of these curves available (and their derivatives and integrals) available in a general-purpose library.<br />
<br />
Hopefully there is some useful information here that helps you with your own programming!<br />
<br />
<br />Anonymoushttp://www.blogger.com/profile/04441790807840642185noreply@blogger.com1tag:blogger.com,1999:blog-4657142000183363795.post-70378997321228527662014-12-15T10:00:00.000-08:002014-12-15T10:00:00.895-08:00QuaternionsQuaternions are an interesting bit of mathematics. You can read the <a href="http://en.wikipedia.org/wiki/Quaternion">Wikipedia article</a> if you really want to understand what they're all about (or, attempt to understand, depending how mathematically inclined you are), but I jut want to talk a bit about using them in games.<br />
<br />
Note: this is only dealing with unit quaternions (i.e. length of 1). In a more generalized case, they can be of any length, but for purposes of expressing rotations in games, they should only be unit length. <br />
<br />
<br />
<br />
<h3>
Transformations</h3>
<br />
<br />
When dealing with geometry transformations, we generally need to worry about three things:<br />
<ul>
<li>Translation (change in position)</li>
<li>Rotation (change in orientation)</li>
<li>Scale (change in size)</li>
</ul>
<br />
In games, we often use a <a href="http://en.wikipedia.org/wiki/Transformation_matrix#Affine_transformations">4x4 affine transformation matrix</a>, as this is very efficient for most hardware to work with. You can combine translation, rotation and scale into a single matrix. You can also store this as a 4x3 matrix, as the last row is always (0, 0, 0, 1).<br />
<br />
Even better, if you have multiple transformations you want to apply, you just multiply all of the matrices together and get a single matrix that does all of them at once. This happens a lot in character animation, where the finger is positioned relative to the hand, the hand is relative to the wrist, the wrist is relative to the elbow, the elbow is relative to the shoulder, and so on.<br />
<br />
Quaternions are another way of specifying the rotation. Note that it is only rotation, so if you want to store scale and translation, you need to store those separately. This is still very space-efficient. Even using four-component vectors for each, it's still the same size as a 4x3 matrix. If you don't need scale, it's 4x2.<br />
<br />
<br />
<h3>
Notation</h3>
<br />
<br />
There is a lot written about quaternions, but I find a lot of the articles to be pretty confusing. When talking about programming, a quaternion and vector are essentially identical from a data storage point of view.<br />
<br />
I put a quaternion into an (x, y, z, w) vector like this:<br />
<br />
x = axis.x * sin(angle/2)<br />
y = axis.y * sin(angle/2)<br />
z = axis.z * sin(angle/2)<br />
w = cos(angle/2)<br />
<br />
where 'axis' is the (unit) vector you want to rotate around, and 'angle' is the angle to rotate.<br />
<br />
What I find most confusing is people putting the 'cos(angle/2)' component in the first component of the quaternion/vector. Mathematically there's nothing wrong with this, as long as you're consistent.<br />
<br />
In Real Life, this causes a lot of confusion when you want to multiply a vector that has been stored as (x, y, z, w) with a quaternion that has been stored (w, x, y, z), so don't store it that way. It's worse when articles have labeled the components (q0, q1, q2, q3) and it's not at all obvious what has been stored where.<br />
<br />
<br />
<h3>
Inspection</h3>
<br />
The way quaternions store data, it's possible to get some intuitive understanding of the rotation it represents by inspecting the values.<br />
<br />
From the axis/angle assignment above, if angle=0, you get:<br />
(sin(0), sin(0), sin(0), cos(0)) =<br />
(0, 0, 0, 1)<br />
<br />
Which is the identity quaternion rotation - if you see (0, 0, 0, 1), there is no rotation. <br />
<br />
For
any other rotation, the (x, y, z) components will be some non-zero
scale of the rotation axis, so inspecting the (x, y, z) components will
give you some idea what axis it's rotating around.<br />
<br />
If you remember your trigonometry, the amount of rotation should be obvious too:<br />
<ul>
<li>w = 1 = cos(0/2) means angle=0</li>
<li>w = 0.7071 = 1/sqrt(2) = cos((pi/2)/2) means angle=pi/2, or a quarter turn</li>
<li>w = 0 = cos(pi/2) means angle=pi, or a half turn</li>
</ul>
<br />
<h4>
</h4>
<h3>
Operations</h3>
<br />
Quaternion multiplication is completely unintuitive, :<br />
q0 * q1 =<br />
( q0.w*q1.x + q0.x*q1.w + q0.y*q1.z - q0.z*q1.y,<br />
q0.w*q1.y - q0.x*q1.z + q0.y*q1.w + q0.z*q1.x,<br />
q0.w*q1.z + q0.x*q1.y - q0.y*q1.x + q0.z*q1.w,<br />
q0.w*q1.w - q0.x*q1.x - q0.y*q1.y - q0.z*q1.z )<br />
<br />
<a href="http://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/transforms/index.htm">Rotating</a> a vector (v = (x, y, z, 0)) by a quaternion (q) is fairly straightforward:<br />
<br />
<br />
v' = inverse(q) * v * q<br />
<br />
The inverse of the quaternion (x, y, z, w) is (-x, -y, -z, w).<br />
<br />
<br />
Even if you are storing scale and translation, this is fairly easy:<br />
<ul>
<li>scale = (sx, sy, sz) </li>
<li>rotation = (qx, qy, qz, qw) </li>
<li>translation = (tx, ty, tz)</li>
<li>scale_inverse = (1/sx, 1/sy, 1/sz)</li>
<li>rotation_inverse = (-qx, -qy, -qz, qw)</li>
<li>translation_inverse = rotate(rotation_inverse, scale_inverse * (-tx, -ty, -tz))</li>
</ul>
Compare with the <a href="http://www.euclideanspace.com/maths/algebra/matrix/functions/inverse/fourD/index.htm">inverse of an arbitrary 4x4 matrix</a> and you'll understand how much more straightforward this is.<br />
<br />
<br />
<h3>
Final thoughts</h3>
<br />
There are a lot of situations where using quaternions can be very helpful - particularly if you need to take the inverse of a transform very frequently. Having your transforms broken out into separate scale / rotation / translation components can also be very beneficial, depending on how you're animating these values.<br />
<br />
This doesn't mean quaternion rotations should be used everywhere - matrix transformations are certainly sufficient. You will almost certainly need a matrix for rendering, and converting back and forth between quaternions and matrices is not trivial.<br />
<br />Anonymoushttp://www.blogger.com/profile/04441790807840642185noreply@blogger.com0tag:blogger.com,1999:blog-4657142000183363795.post-39530386028786715352014-12-02T15:44:00.001-08:002014-12-02T15:44:14.352-08:00Random Blog DayOn my mind recently:<br />
<br />
I watched @Jonathan_Blow live-coding on twitch.tv - he has been working on his own language, and it's nice to watch someone else have mind-bending code generation problems for a change. :)<br />
<br />
I have a lot of interest in game development languages, though I have primarily focused on scripting, as opposed to an entire game development language. It's probably a little vague as to which category some things should drop into. <br />
<br />
Conceptually, I like to think of scripting as writing custom code for individual game objects. This doesn't make it strictly game-specific, but it probably tends to be. Ultimately, it lets you glue all of the bits of the game engine together into something new. Also, having some syntactic control can let you specify certain this more easily. For instance, I have special notation to convert musical beat/measure values into actual time values, and ways to specify timing, sequencing and interpolation. It could be done in C, but the point is to make it easier to read and write than make it possible.<br />
<br />
Engine coding is a fairly different beast. First, you probably have to talk to third party libraries, even if that's just DirectX, OpenGL, basic operating system, etc. So, you're probably stuck at some point with whatever language those are written in. For simpler games you can get away with a higher level language, but for maximum performance and compatability, you're still looking at C/C++.<br />
<br />
One interesting idea is to have a sort of pre-compiler that spits out C/C++ code. I've seen similar things done before, to varying degrees of success. This is pretty much guaranteed to degrade the already dubious quality of debuggers and debugging, but it's a way to add language features that are otherwise hard to get. For instance, pre-hashed strings.<br />
<br />
-----<br />
<br />
<br />
In other news, the new World of Warcraft expansion Warlords of Draenor is out. It is good. :) They have done a lot of work scripting out quests, and there are a lot of places with a huge number of NPCs wandering around, which helps to bring the world to life a lot more than I remember seeing before. Some of the battles are truly epic.<br />
<br />
You have your own garrison now, which is a pretty cool idea, although we'll have to see how some of the features play out. Since everyone is doing stuff in their own garrison, you don't see other people as much. As a hardcore solo player, it doesn't affect me much, just feels a bit weird for an MMO. <br />
<br />
You can also build buildings to give you access to the various professions, including ones you haven't taken on yourself. On one hand, this seems like a good idea, as it gives you access to some of the crafting you otherwise wouldn't have. On the other, you can't have all of the buildings at once, and you probably want the ones you do have the professions for, so your choices are kind of limited.<br />
<br />
<br />
-----<br />
<br />
That's all for now - sorry I haven't been posting recently. Too much going on, not enough stuff that makes good blog posts. :)<br />
<br />Anonymoushttp://www.blogger.com/profile/04441790807840642185noreply@blogger.com0tag:blogger.com,1999:blog-4657142000183363795.post-31069119847878294872014-10-31T10:07:00.000-07:002014-10-31T10:07:03.027-07:00Python InjectionToday's fun stupid code is written in Python.<br />
<br />
I have found a few times recently where it was handy to either replace or inject code in specific C++ files, or even level files. I have done this sort of thing in AWK in the past, but AWK is, well, awkward.<br />
<br />
The basic pattern looks like this:<br />
<br />
<!-- HTML generated using hilite.me --><br />
<div style="background: #ffffff; border-width: .1em .1em .1em .8em; border: solid gray; overflow: auto; padding: .2em .6em; width: auto;">
<pre style="line-height: 125%; margin: 0;"><span style="color: blue;">import</span> tempfile
src = "yourfile.txt"
<span style="color: blue;">with</span> tempfile.TemporaryFile(<span style="color: #a31515;">"w+"</span>) <span style="color: blue;">as</span> out:
<span style="color: blue;">with</span> open(src, <span style="color: #a31515;">"rU"</span>) <span style="color: blue;">as</span> f:
<span style="color: blue;">for</span> line <span style="color: blue;">in</span> f:
<span style="color: blue;">if</span> line.find(<span style="color: #a31515;">'REPLACE ME'</span>) != -1:
out.write(<span style="color: #a31515;">'REPLACE WITH'</span>)
<span style="color: blue;">else</span>:
out.write(line)
<span style="color: green;">#Beginning of temp file</span>
out.seek(0)
<span style="color: green;">#Copy temp file over source</span>
<span style="color: blue;">with</span> open(src, <span style="color: #a31515;">"w"</span>) <span style="color: blue;">as</span> f:
<span style="color: blue;">for</span> line <span style="color: blue;">in</span> out:
f.write(line)
</pre>
</div>
<br />
<br />
This just opens a temporary file, scans every line of the source file and writes it to the temporary file, with possible modifications. Then it jumps back to the beginning of the temporary file and overwrites the source file with the temporary file.<br />
<br />
It's probably a good idea to have whatever you're modifying backed up somewhere <br />
<br />
It may be easier to just read the entire file into a list of strings and write that back out, but now you know how to use temporary files. :)<br />
<br />
What you do at the 'REPLACE ME' line is up to you - check for a specific sub-string, or exact line. Similarly for the 'REPLACE WITH' line - replace the line with generated code, insert a new line of code and put the 'REPLACE ME' marker back in as a comment, etc.<br />
I have one version of this that copies a component template .h and .cpp file, replaces 'XXXXX' in the new files with the new component name, and injects the component registration code into the game initialization function. Creating a new component used to be a chore, now it's easy!<br />
<br />Anonymoushttp://www.blogger.com/profile/04441790807840642185noreply@blogger.com0tag:blogger.com,1999:blog-4657142000183363795.post-3028545032899721172014-10-16T17:00:00.000-07:002014-10-16T17:00:04.414-07:00refptrI come across this programming pattern every once in a while:<br />
<br />
<ol>
<li>In an object's constructor, allocate some data</li>
<li>When copying the object, copy the pointer to the allocated data, rather than duplicate all of the data</li>
<li>Delete the data in the object's destructor </li>
</ol>
<br />
Of course, this ends in disaster, because the first of these objects that gets destroyed deletes the data, leaving all of the others holding an invalid pointer.<br />
<br />
Some types of smart pointer let you pass "ownership" of the data when copying an object by only letting one object hold the pointer at a time, but this is very limiting. (<a href="http://www.cplusplus.com/reference/memory/auto_ptr/">auto_ptr</a>)<br />
<br />
An alternative is to have a smart pointer that holds the "master" allocation, and all others get a regular copy of the pointer. This works, but doesn't really fix the above problem where there is no obvious master allocation.<br />
<br />
One of the best generic solutions I have seen is to have an intermediate reference counting class that all of the objects point to, instead of having them point directly at the data. This causes an extra level of indirection when you want to dereference a pointer, but it solves the above problem without any manager classes, ownership passing, etc. (<a href="http://www.cplusplus.com/reference/memory/shared_ptr/">shared_ptr</a>)<br />
<br />
So, something like this:<br />
<br />
<!-- HTML generated using hilite.me --><br />
<div style="background: #ffffff; border-width: .1em .1em .1em .8em; border: solid gray; overflow: auto; padding: .2em .6em; width: auto;">
<pre style="line-height: 125%; margin: 0;"><span style="color: blue;">template</span> <<span style="color: blue;">typename</span> T>
<span style="color: blue;">class</span> <span style="color: #2b91af;">refptrsentinel</span>
{
<span style="color: blue;">template</span> <<span style="color: blue;">typename</span> U> <span style="color: blue;">friend</span> <span style="color: blue;">class</span> <span style="color: #2b91af;">refptr</span>;
refptrsentinel(T *obj) : ptr(obj), count(1) { }
~refptrsentinel() { <span style="color: blue;">delete</span> ptr; }
<span style="color: #2b91af;">void</span> acquire() { ++count; }
<span style="color: #2b91af;">void</span> release() { <span style="color: blue;">if</span>(--count == 0) <span style="color: blue;">delete</span> <span style="color: blue;">this</span>; }
T *ptr;
<span style="color: #2b91af;">int</span> count;
};
<span style="color: blue;">template</span> <<span style="color: blue;">typename</span> T>
<span style="color: blue;">class</span> <span style="color: #2b91af;">refptr</span>
{
public:
refptr() : sentinel(NULL) { }
refptr(<span style="color: blue;">const</span> refptr &rhs) { sentinel = rhs.sentinel; <span style="color: blue;">if</span>(sentinel) sentinel->acquire(); }
refptr(T *obj) { sentinel = obj ? <span style="color: blue;">new</span> refptrsentinel<T>(obj) : NULL; }
~refptr() { <span style="color: blue;">if</span>(sentinel) sentinel->release(); }
refptr &<span style="color: blue;">operator</span> =(<span style="color: blue;">const</span> refptr &rhs)
{
<span style="color: blue;">if</span>(sentinel)
sentinel->release();
sentinel = rhs.sentinel;
<span style="color: blue;">if</span>(sentinel)
sentinel->acquire();
<span style="color: blue;">return</span> SELF;
}
<span style="color: blue;">operator</span> <span style="color: #2b91af;">bool</span>() <span style="color: blue;">const</span> { <span style="color: blue;">return</span> sentinel != NULL; }
<span style="color: #2b91af;">bool</span> <span style="color: blue;">operator</span> !() <span style="color: blue;">const</span> { <span style="color: blue;">return</span> sentinel == NULL; }
T &<span style="color: blue;">operator</span> *() <span style="color: blue;">const</span> { <span style="color: blue;">return</span> sentinel->ptr; }
T *<span style="color: blue;">operator</span> ->() <span style="color: blue;">const</span> { <span style="color: blue;">return</span> sentinel->ptr; }
private:
refptrsentinel<T> *sentinel;
};
</pre>
</div>
<br />
Anonymoushttp://www.blogger.com/profile/04441790807840642185noreply@blogger.com0tag:blogger.com,1999:blog-4657142000183363795.post-75055684453220673492014-10-09T16:19:00.000-07:002014-10-11T11:32:13.215-07:00Light 'em upIt's lighting week.<br>
<br>
First off, Blender's new Cycles render engine is pretty spiffy. Path tracing just makes lighting look so nice. Plus it's more or less interactive, as it can do a very low-res pass fairly quickly as you're moving the camera around.<br>
<br>
Even better, Blender 2.7 added texture baking, so all of that yummy lighting goodness can be used to make lightmaps and such.<br>
<br>
So far, the pipeline to convert a Blender shader graph into a GPU vertex/fragment shader has worked really well. Obviously there are some limitations - path tracing and triangle rasterization are just completely different ways of doing things - but I can build up a complex set of materials and more or less get the same thing in the game.<br>
<br>
Here's a ship in Blender: <br>
<br>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgNzybIKV1rsjLccWbuMSFU1MJm_87538yXQQUfpMb_pRrgowK_8A0jEn22PC3kZe23qFwK_0QYuWO2-LRYweRjHm_b1r56lCCPEtn_wWhDyyuvTrlI9pgO6ik9VGz8B8Tl9CBmpArCsg/s1600/swarmer.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgNzybIKV1rsjLccWbuMSFU1MJm_87538yXQQUfpMb_pRrgowK_8A0jEn22PC3kZe23qFwK_0QYuWO2-LRYweRjHm_b1r56lCCPEtn_wWhDyyuvTrlI9pgO6ik9VGz8B8Tl9CBmpArCsg/s1600/swarmer.png" height="360" width="640"></a></div>
<br>
And in-engine:<br>
<span id="goog_910848219"></span><span id="goog_910848220"></span><br>
<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjRqAGX90GTTvH6Up448doDr8NzFuvMLs-6nxH89Y_G7kHUxJ0eQlh41y7DgJh9uFPCDoIMsT740z6kzEDbZjuVTf-ThWqaA8Tghvvam7pqyzVQa-8c81Z1XvzDXxMFJf76NvgHtYFhVg/s1600/swarmer_ig.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjRqAGX90GTTvH6Up448doDr8NzFuvMLs-6nxH89Y_G7kHUxJ0eQlh41y7DgJh9uFPCDoIMsT740z6kzEDbZjuVTf-ThWqaA8Tghvvam7pqyzVQa-8c81Z1XvzDXxMFJf76NvgHtYFhVg/s1600/swarmer_ig.png" height="360" width="640"></a></div>
<span id="goog_910848219"></span><span id="goog_910848220"></span><br>
<span id="goog_910848219"></span><span id="goog_910848220"></span><br>
Not exactly the same, but similar. It looks a little washed out, some of that is the bloom effect not present in the render.<br>
<br>
This is using lighting based on the skybox - spherical harmonics for diffuse, plus a biased mip map sample to get a blurred / rough specular reflection. It looks pretty nice in motion. The emissive materials help as well.<br>
<br>
Self-illumination and ambient occlusion maps would help a bit. It may also be worth generating point/directional lights for hot spots in the skybox to give some added definition to the normal maps.<br>
<br>
Some day we'll be doing real-time path tracing (or something similar), and it's going to be awesome. :)<br>
<br>
Points of note:<br>
<ul>
<li>Using the shader instruction texCUBEbias(...) to choose a "blurred" mip level was not working properly. The documentation on this instruction is pretty vague, but I kept getting really strange results. texCUBElod(...) seems to do what I want.</li>
<li>The auto-generated mip maps for the cube map definitely have seams at lower resolutions. This seems a little tricky to fix, I may just live with it for now.</li>
<li>Using D3DXSHProjectCubeMap(...) to generate the spherical harmonic coefficients was not working as expected - it looks like x and y are both negated? Anyway, negating coefficients where the sign differences don't cancel out gives light coming from the right direction.</li>
</ul>
That's all for now, have a great week!<br>
<br>Anonymoushttp://www.blogger.com/profile/04441790807840642185noreply@blogger.com0tag:blogger.com,1999:blog-4657142000183363795.post-68872134058093861012014-10-03T17:00:00.000-07:002014-10-11T11:31:47.917-07:00Getting the MessageHello long-neglected blog viewers!<br>
<br>
I won't get into a lengthy discussion about why I haven't posted for ages, but it sounds a lot like, "I can't post that because it's spoilers, I can't post that because it's boring, I can't post that because I'm busy working, I can't post that because I'm not at my computer, and I can't post THAT because... well, that raid's not going to heal itself now, is it?"<br>
<br>
The short version: Game is still in progress. Engine is kind of nice. Music is coming along. Level design is not there yet, but making definite progress.
I want to show stuff, but greyblock art is maybe not a good first impression, so I'm working on some prettying up.<br>
<br>
Short-ish term goal: Get a single level together for design and engine testing. <br>
<br>
So, let's talk code.<br>
<br>
<!-- HTML generated using hilite.me --><br>
<div style="background: #ffffff; border-width: .1em .1em .1em .8em; border: solid gray; overflow: auto; padding: .2em .6em; width: auto;">
<pre style="line-height: 125%; margin: 0;"><span style="color: #008800; font-weight: bold;">typedef</span> <span style="color: #0066bb; font-weight: bold;">void</span> (<span style="color: #333333;">*</span>Fn)(<span style="color: #008800; font-weight: bold;">const</span> string <span style="color: #333333;">&</span>);
<span style="color: #008800; font-weight: bold;">typedef</span> VSTACK<span style="color: #333333;"><</span>Fn<span style="color: #333333;">></span> FnList;
<span style="color: #008800; font-weight: bold;">typedef</span> MAP<span style="color: #333333;"><</span>string, FnList<span style="color: #333333;">></span> StringFnMap;
<span style="color: #008800; font-weight: bold;">class</span> <span style="color: #bb0066; font-weight: bold;">cMessageHandler</span>
{
FnMap mFnMap;
<span style="color: #997700; font-weight: bold;">public:</span>
<span style="color: #333399; font-weight: bold;">void</span> <span style="color: #0066bb; font-weight: bold;">Register</span>(<span style="color: #008800; font-weight: bold;">const</span> string <span style="color: #333333;">&</span>name, Fn fn)
{
mFnMap[name].push_back_resize(fn);
}
<span style="color: #333399; font-weight: bold;">void</span> <span style="color: #0066bb; font-weight: bold;">Unregister</span>(<span style="color: #008800; font-weight: bold;">const</span> string <span style="color: #333333;">&</span>name, Fn fn)
{
mFnMap[name].find_erase(fn);
}
<span style="color: #333399; font-weight: bold;">void</span> <span style="color: #0066bb; font-weight: bold;">Call</span>(<span style="color: #008800; font-weight: bold;">const</span> string <span style="color: #333333;">&</span>name, <span style="color: #008800; font-weight: bold;">const</span> string <span style="color: #333333;">&</span>s)
{
FnList <span style="color: #333333;">&</span>f <span style="color: #333333;">=</span> mFnMap[name];
<span style="color: #008800; font-weight: bold;">for</span>(FnList<span style="color: #333333;">::</span>ITER it <span style="color: #333333;">=</span> f.begin(), end <span style="color: #333333;">=</span> f.end(); it <span style="color: #333333;">!=</span> end; <span style="color: #333333;">++</span>it)
{
(<span style="color: #333333;">*</span>it)(s);
}
}
};
</pre>
</div>
<br>
<br>
Basically, it's just a list of functions listening to a particular message ID (string), and a function to broadcast a message (string) to each of them. It's using my own containers, but you could use std::vector and std::map easily enough instead.<br>
<br>
I also have a version that handles member functions - you just need
to store the "this" pointer and a redirect function for each class,
similar to a delegate.<br>
<br>
This makes it pretty easy to send messages or triggers between components from data - there's no need to create a message ID or bind to specific functions in each class. Instead of writing a bunch of custom code for each class to bind it to other components, they just need to send or listen to the same message name.<br>
<br>
My project has a "game" library, which has many generic game components in it. It is not supposed to have any game-specific code or logic in it to make it easier to re-use in a new project. This was causing some problems connecting generic components to game-specific components, but this system lets me hook them up in data without the game library knowing anything about the game-specific stuff.<br>
<br>
Initially I found this system kind of worrying, mostly because it involves so many strings. It's the old-school hacker mentality, where individual bytes and cycles were of vital importance. Well, you still probably need to be a little gentle with it - it's for events that happen only occasionally. Overall, I think it's worth being able to easily bind events from data.<br>
<br>
So, yes, still working on things. :) I will try to update more often, not sure of the frequency yet!<br>
<br>Anonymoushttp://www.blogger.com/profile/04441790807840642185noreply@blogger.com0tag:blogger.com,1999:blog-4657142000183363795.post-65730567002601387322014-03-26T15:03:00.001-07:002014-03-26T15:03:42.983-07:00Announcing iColorToolBase Metal Games has its first app!<br />
<br />
<a href="https://itunes.apple.com/ca/app/icolortool/id839219434?mt=8">iColorTool</a> is an iOS color identification / video processing app to help those with color blindness.<br />
<br />
<a href="https://itunes.apple.com/ca/app/icolortool/id839219434?mt=8&uo=4" target="itunes_store" style="display:inline-block;overflow:hidden;background:url(https://linkmaker.itunes.apple.com/htmlResources/assets/en_us//images/web/linkmaker/badge_appstore-lrg.png) no-repeat;width:135px;height:40px;@media only screen{background-image:url(https://linkmaker.itunes.apple.com/htmlResources/assets/en_us//images/web/linkmaker/badge_appstore-lrg.svg);}"></a>
<br />
<br />
The first mode, color identification, takes the average color from the center of the camera view and tries to match it to the nearest color in a set of known colors.<br />
<br />
There are multiple video processing modes. The simulation modes can help those with normal color vision understand how the world appears to those with color blindness. The error and color channel modes can help those with color blindness see color information that they might be missing.<br />
<br />
In the next few days, I'll try to post a few thoughts on the app development experience!<br />
<br />Anonymoushttp://www.blogger.com/profile/04441790807840642185noreply@blogger.com0tag:blogger.com,1999:blog-4657142000183363795.post-3447578608164451792014-01-13T16:44:00.000-08:002014-01-13T16:44:00.030-08:00Back Into the FrayToday was back into coding land a little bit.<br />
<br />
Mostly it has just been cleaning up the way I'm managing the UI for characters. Since I actually have proper game assets now (yay) I discovered that the way I was handling multiple facial expressions to be a bit lacking.<br />
<br />
This also led to some changes to the component system, allowing me to "include" an external text file for individual component definitions. This was prompted by not wanting to duplicate the layout for the UI into every level file.<br />
<br />
So, between those things, and the inevitable bugs from taking everything apart and putting it back together again, the day is pretty much done.<br />
<br />
I have also clearly not been practicing the guitar enough. I seem to practice in one or two week stretches and get back to the point where my hand doesn't feel like it's going to fall off after playing power chords for 20 minutes... and then promptly take a month off from playing and go back to square one again.<br />
<br />
Back to it then...<br />
<br />Anonymoushttp://www.blogger.com/profile/04441790807840642185noreply@blogger.com0tag:blogger.com,1999:blog-4657142000183363795.post-63233371024083587372014-01-07T10:07:00.000-08:002014-01-07T10:07:34.680-08:00The Blog That Time ForgotMy last post was Dec. 19?<br />
<br />
Christmas season is obviously not my most productive time of year. That's not to say I haven't been working (although there was a lot of World of Warcraft going on), but the hours have been kind of weird.<br />
<br />
Since the last update, I have mostly been doing stuff that doesn't blog well. Some UI stuff, editor stuff, story stuff, and level stuff. Blogging about those would be boring, boring, spoilers, and boring spoilers, respectively.<br />
<br />
Or maybe you're really into greyblocked spaceship placement. This is for you guys:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiuSc2tEIZFHaRLv79OG9RaAyhnHu3KDa_KwwATgQ24wO0KQG8ZIqykHvdCzczgh7-kFQxvbFhwiIVA-QZkXTfCt5zUxWpQ9widdoVPAEjbJ9NHwK7TmB9y6BU-KuDqNiLKnx_3z0MdGg/s1600/greyblock.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiuSc2tEIZFHaRLv79OG9RaAyhnHu3KDa_KwwATgQ24wO0KQG8ZIqykHvdCzczgh7-kFQxvbFhwiIVA-QZkXTfCt5zUxWpQ9widdoVPAEjbJ9NHwK7TmB9y6BU-KuDqNiLKnx_3z0MdGg/s1600/greyblock.jpg" height="180" width="320" /></a></div>
<br />
<br />
Probably the most interesting thing was the addition of free-flight mode. Up to this point, the levels have essentially been built on a rail - the user can move around inside the camera's view, but the camera is on a fixed animation path. I had been planning on doing this for ages but was too focused on other things to get around to it.<br />
<br />
So far it feels really good. I'm still trying to figure out the camera angle. It feels really good to have the camera down low, but then it's way too obvious that the whole world is built on a 2D plane. If you pull the camera up, you can't see far enough ahead of the ship. I'm sure I'll work something out.<br />
<br />
Next up is a bit more enemy AI, and probably a little more level editor UI stuff. I did manage to get parts of the UI back from the Profound State of Brokenness that I somehow put it into a couple months ago. Even Perforce history couldn't rescue me from that one. There were way too many "Why doesn't this work? How did this used to work? HOW DID THIS EVER WORK????" moments.<br />
<br />Anonymoushttp://www.blogger.com/profile/04441790807840642185noreply@blogger.com0tag:blogger.com,1999:blog-4657142000183363795.post-30471839847136334522013-12-19T11:52:00.001-08:002013-12-19T11:52:27.958-08:00SlackOK, I've been slacking on daily updates again. :)<br />
<br />
There hasn't been a lot of bloggable stuff going on, though. I've mostly been focusing on greyblocking all of the levels and putting dialog in.<br />
<br />
I made a few additions to the level editor to support some of the things I was doing. I added clone/duplicate to the editor, which has made placing multiple objects a lot easier. This was pretty easy to do because cloning is basically an inherent feature in the game object / component system.<br />
<br />
There are still some changes that I need to make there. Most importantly, I need a better way to select objects. The mouse clicking actually works OK right now, except I'm not displaying bounding boxes for everything, so sometimes it's hard to know what you're clicking on. <br />
<br />
Overlapping objects are a bit of a pain. What I ended up doing was, when you click on multiple overlapping objects, you select the one with the smallest bounding box. Most of the time this actually seems to do what you expect.<br />
<br />
Some bounding boxes overlap completely, though. Worse, some objects really don't have a bounding box because they're either points or just stuff in the scene that's doing stuff that doesn't really exist at a specific location in space (such as the main music track). These all just kind of overlap at the origin.<br />
<br />
My UI controls are kind of in a state of brokenness. I suppose I should just fix them, then I can choose from a list of objects. I had started work on a tree view, but didn't get it finished yet. Or I can do the hacker thing and put in next/prev object keys.<br />
<br />
Sounds like I have code to write, yay! :) I like doing level design and dialog, but it doesn't flow as easily as code.<br />
<br />
<br />
Oh, and after nine years of purposefully avoiding it, World of Warcraft has finally got its hooks into me. So far it's only cutting into lunch and overtime hours. :)<br />
<br />Anonymoushttp://www.blogger.com/profile/04441790807840642185noreply@blogger.com3tag:blogger.com,1999:blog-4657142000183363795.post-9486437081429361892013-12-10T19:11:00.001-08:002013-12-10T19:11:55.815-08:00Weak ParentBlogging on the go today. Typing a whole post with the iPhone keyboard is far from fun, but not much choice today.<div><br></div><div>Today started with a little modeling work. There were a couple "vents" on one of my spacecraft I wasn't too keen about. I'm still not liking it too much, but it's better. I'll probably have another go at it tomorrow and maybe get an image up.</div><div><br></div><div>Most of the rest of the day was trying to get everything in place to allow proper level transitions. It's about time to rough in all of the planned levels, but currently things are a bit debuggish and I can't currently chain level loads.</div><div><br></div><div>I also put in a new "weak parent" component that basically just copies the transform of one game object to or from another. After parenting the game camera to the ship, I discovered that the game stops rendering when the player ship explodes because the camera goes with it as the child of the ship. I suppose you can debate whether this is a good arrangement, or if children should just be reparented. This way makes a little more sense to me, and adding reparenting as a separate operation from destruction would work fine.</div><div><br></div><div>So, busy day, working towards getting levels roughed in. More of the same tomorrow.</div>Anonymoushttp://www.blogger.com/profile/04441790807840642185noreply@blogger.com0tag:blogger.com,1999:blog-4657142000183363795.post-90518339581096943172013-12-09T18:24:00.001-08:002013-12-09T18:24:52.160-08:00Gray DayAfter a weekend of Bioshock: Infinite (which was just excellent, in case you are like me and way, WAY behind the times) it's time to get back to work.<br />
<br />
After doing a bunch of art-related stuff this morning, I decided to get back to level creation stuff.<br />
<br />
Using the drum track of the level's music last week was a good start, but needed to introduce some more randomness into the equation.<br />
<br />
I assigned a spawn range to each drum note and then used a <a href="http://en.wikipedia.org/wiki/Gray_code">Gray code</a> to determine a spawn position inside each range. This is a little nicer than just using completely random numbers, because you're guaranteed to not have a repeated number (each new number differs from the previous by exactly one bit). But it's also gives the appearance of randomness on the scale that I'm working at.<br />
<br />
Result: pretty fun, actually. :) New problem: it's pretty difficult, too; will need some testing to determine if it's appropriate for just the second level.<br />
<br />
Oh, and the art related stuff. Kicking around "movie poster" type ideas. I was looking at a bunch of <a href="http://www.michalkrasnopolski.com/classic-movies.html">minimalist movie posters</a> and thought they were quite striking. This is not quite so minimalist, and will probably make no sense to you at the moment, but hopefully it stands out:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgwcpZo37907SYQWHzNc93mxNv86be6xsiFEHAG0-o6Uql-rHxQooLmJtT7gD8h14XgUQ-fciqJCyrWKXnR6EkMv7drolTU7WBMnsOxl7ifb207GuynzousUt36V8acA7fW54lyw1JYIA/s1600/understand.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="400" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgwcpZo37907SYQWHzNc93mxNv86be6xsiFEHAG0-o6Uql-rHxQooLmJtT7gD8h14XgUQ-fciqJCyrWKXnR6EkMv7drolTU7WBMnsOxl7ifb207GuynzousUt36V8acA7fW54lyw1JYIA/s400/understand.png" width="400" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
</div>
<br />Anonymoushttp://www.blogger.com/profile/04441790807840642185noreply@blogger.com0tag:blogger.com,1999:blog-4657142000183363795.post-37820429583007739342013-12-06T17:19:00.002-08:002013-12-06T17:19:20.414-08:00"End of stream" means something else apparentlyTrying to get the audio synchronization to work.<br />
<br />
I was having some trouble last time I looked at it with getting a proper sample cursor position on the stream voice. The music stream gets read in chunks, so there are multiple buffers getting filled and submitted to the audio system. I seemed to be having the problem that every time a buffer was started, the current play position was reset, even though the docs said it should be the count until an end of stream marker is reached.<br />
<br />
Debugging audio can be a pain in the butt, because there are all sorts of things all happening at the same time with multiple threads, hardware interrupts, etc. It's hard to debug things in a properly "stopped" state sometimes, because every time you step an instruction, all sorts of other stuff happens.<br />
<br />
In any case, the problem should have been obvious: the end of stream marker doesn't actually stop anything, it just keeps playing submitted buffers happily. And the end of stream marker was set on every single buffer.<br />
<br />
Then I got into looking at the shader node stuff in Blender. I was <i>trying </i>to set the transparency based on the dot product of the view vector and the surface normal to give a nice smooth falloff.<br />
<br />
First off, the shader system is completely awesome, because it actually has input pins for normal, view vector, etc., and math nodes for doing dot products and other vector operations. So, in theory, it should actually be possible to compute that.<br />
<br />
Two problems:<br />
<br />
<ol>
<li>Specifying transparency is a bit difficult, because the renderer is using path tracing, so there isn't really an "alpha channel" you can specify for opacity, it's more about probability that each of the R/G/B channels will be passed through.</li>
<li>The above math operations don't seem to be working as expected.</li>
</ol>
Not sure what the problem is trying to get a dot product result. Possibly some difficulty dealing with negative results, maybe the vectors aren't in the same space (camera vs. world?) <br />
<br />
Plus, it seems that different operations might give output on different pins. The dot product will result in a scalar, it would be nice if it output it to all channels of the vector pin or something too. Or was documented <i>at all</i>. Or the source code wasn't so opaque. (haha, opaque... stopping now)<br />
<br />
Oh, I also wrote a tool to convert a MIDI file into script commands to spawn objects a specific time values. This is half way to cool, except drum tracks only have a few different notes, which is presently kind of boring. Still working on a good solution to that.<br />
<br />Anonymoushttp://www.blogger.com/profile/04441790807840642185noreply@blogger.com0tag:blogger.com,1999:blog-4657142000183363795.post-72429173968023678612013-12-05T17:33:00.000-08:002013-12-05T17:33:29.684-08:00Smooth, very smoothBack to work.<br />
<br />
I basically took the last two days to recharge. Motivation and energy have been in short supply recently for some reason, so I took some time to play some games and just record some music for fun. It's nice to be able to take a mental break when you need it, and today has been relatively productive.<br />
<br />
A while back while playing with <a href="https://www.shadertoy.com/">Shadertoy</a>(1) I came across the <a href="http://en.wikipedia.org/wiki/Smoothstep">smoothstep</a> shader command, which for some reason I hadn't come across before. <a href="http://msdn.microsoft.com/en-us/library/windows/desktop/bb509658%28v=vs.85%29.aspx">Microsoft</a>'s documentation is a little opaque, but the Wikipedia article there is quite informative.<br />
<br />
So basically, rather than doing a linear interpolation with lerp(), you can smooth interpolation with smoothstep(). The function it uses is f(x) = 3*x^2 - 2*x^3, which at first glace looks like a bunch of numbers somebody just made up, but as you can read in the article, they're the numbers you come up with if you figure out the coefficients on a generic 3rd order equation that has f(0) = 0, f(1) = 1, f'(0) = 0 and f'(1) = 0. How cool is that?<br />
<br />
You can also do the same thing for a 5th order equation that also has zeros at the second derivative, for a "smootherstep."<br />
<br />
So, I inserted that into my vector and curve libraries, because that's just very useful. :)<br />
<br />
Then, onto some real work.<br />
<br />
I want blue laser "bullets," but they really weren't showing up very well against my background. What I really want is the "lightsaber" look - bright white inner "core" with a blue halo around it. I was trying to put a really bright blue bullet into the scene hoping the bloom filter would blur it out, but that doesn't really give the white core. <br />
<br />
I modified the bullet model to have a white core with a transparent blue shell around it. That's an improvement, but not quite there yet. This uncovered some unimplemented features in the shader graph (the "mix" node wasn't working properly). This also prompted me to fix the alpha sorting, which wasn't properly detecting when transparent shader nodes were being used and setting the correct flags. Hopefully that's it for pipeline work for a while.<br />
<br />
Regardless, I now have a white core... with a blue shell around it. Which looks OK, but doesn't have the falloff I want. Ideally I would adjust the alpha based on the dot product of the view vector and the normal at each pixel to give more falloff at the edges. That's an easy enough shader to write, but I'll need to find a way to specify it in the shader graph.<br />
<br />
Then I wondered what the game would look like if I set the parent of the game camera to the ship rather than the world as I'm fly down and around the "hyperspace tunnel." I think this is going to have to be part of the game.<br />
<br />
<br />
1: In Firefox, I needed to go to about:config and set webgl.prever-native-gl to true, or that site would hang Firefox basically forever and I would have to kill it from the process manager. From what I was able to determine, the translation layer that converts from WebGL to DirectX doesn't work very well on some code.<br />
<br />Anonymoushttp://www.blogger.com/profile/04441790807840642185noreply@blogger.com0tag:blogger.com,1999:blog-4657142000183363795.post-67703141168341896472013-12-02T17:26:00.000-08:002013-12-02T17:26:38.190-08:00I Used To See Four LightsSo, what's going on today...<br />
<br />
I decided to put some proper lighting code into the component system. I've been using the same four hard-coded lights for quite some time, and it seemed like maybe now was the time to do something a little better. :)<br />
<br />
So, adding in a couple of light components. First, the lights themselves, which are attached to a transform and can therefore be attached to other objects or otherwise moved around the scene. And second, a collector, which can be attached to a model object and determine the influence of the lights in the scene on the object.<br />
<br />
This is pretty basic, we'll see how well it works out. Currently I'm just accumulating all of the lights into a single set of spherical harmonic coefficients for each object. That might become performance-prohibitive eventually, though any sort of dynamic spatial partitioning probably won't be cheap either.<br />
<br />
So, today was mostly spent brushing up on the math for all of that again... and then remembering that DirectX has a bunch of functions for this already... and then remembering that I already wrote code that was using them months ago. I seriously need to defrag my brain or something.<br />
<br />Anonymoushttp://www.blogger.com/profile/04441790807840642185noreply@blogger.com0tag:blogger.com,1999:blog-4657142000183363795.post-68611506467745117102013-11-28T10:52:00.001-08:002013-11-28T10:52:54.967-08:00PushingSo many things going on all at once... not sure which of them might be interesting. :)<br />
<br />
The last couple of days have been spent jumping around all over the place:<br />
<ul>
<li>Reviewing concept character art - I now know what my lead characters look like (!)</li>
<li>Writing character dialog </li>
<li>Working on the "hyperspace" level design, resurrecting some old test code</li>
<li>Arranging music </li>
<li>Adding "cylinder" primitive drawing to the render library</li>
<li>Putting local variables into the script system </li>
<li>Music sync and object spawn sync</li>
<li>Dealing with physics issues</li>
</ul>
The physics issues were perhaps the biggest problem. Currently, Blender doesn't let you set the mass of anything to more than 10000 units (say, kg) and not less than something like 0.01. This was causing some problems with low-mass bullets pushing high-mass objects around in unpleasing ways simply because they move fast enough to carry a lot of energy.<br />
<br />
I tried a few different methods for dealing with this, but none of them were working very well. Disabling collision response kind of works, but isn't selective enough and actually lets some objects pass through each other that I do want to respond.<br />
<br />
I tried making the object Kinematic, which means it exists in the collision system but doesn't have it's motion affected by collisions. Unfortunately, in Bullet, it doesn't actually have any built-in motion of any sort, it requires game-side key-frame animation, where I just wanted to set a velocity and have it go.<br />
<br />
So, I ended up just making it a regular dynamic rigid body with a really, really large mass. This may cause weird numerical instability, but my immediate tests seem to work just fine - it doesn't get pushed around by bullets, and nudges the player ship out of the way during collisions.<br />
<br />
Then, I was trying to get a constraint to work in the physics system, but it was all going horribly wrong... until I finally figured out that some code forcing the ship orientation was causing the constraint to be violated. Removing that and just letting physics "do its thing" fixed everything nicely.<br />
<br />
<br />
I would say there has been some real progress on getting the level done. Getting music, spawning and rendering all in sync still needs some work, but the pieces are really falling into place now.Anonymoushttp://www.blogger.com/profile/04441790807840642185noreply@blogger.com0tag:blogger.com,1999:blog-4657142000183363795.post-6102761240278398432013-11-25T16:53:00.000-08:002013-11-25T16:53:45.754-08:00Sonic Screwed Up DriverIt was a Doctor Who weekend - meaning, I didn't exactly get a great deal done. That is to say, I got an immense amount of "research" done, but not a lot else.<br />
<br />
I did listen to a few of the bits of music I put together last week several times. It's vitally important to listen to things you've been recording at 2:00am with fresh ears in the cold light of day. While I like some of them quite a bit, it will be a while before they're a proper track.<br />
<br />
I put what I have into the engine today anyway. As predicted, horns and strings by themselves show up nicely in the spectrum, while guitar, bass and drums are all kind of squashed into the same frequency band, and drown out everything else. I will need to play with the EQ to see if I can find something that both looks and sounds good.<br />
<br />
In any case, I started doing the script to spawn a few asteroids in time with the music. That's working OK, except for being slightly out of sync. The big problem is that the texture scroll and the object motion seem to be slightly out of phase. Motion in games is inherently jittery, but they're not jittering at the same time, which kind of stands out. This probably has something to do with the texture being updated based on when new audio buffers become available, and the physics is just updated off the system timer. I've been meaning to run the game update time off of the audio play cursor, which will probably help, but I imagine there will be some wrangling to make that work.<br />
<br />
Also, the whole thing could hardly be called "frantic" at this point... which is fine for the first level of trying to ease the player into the game, but will need to be worked out at some point. I had some old gameplay tests with a different camera angle and objects moving faster, I'll need to give that a try.<br />
<br />
Which will happen shortly after I stop finding interesting* stuff on the internet and actually focus for five minutes.<br />
<br />
Which makes game development kind of like a Doctor Who episode. All running about and getting distracted by nonsense, and then five minutes of brilliance saves the day. Most of the time.<br />
<br />
<br />
<br />
*not actually interesting, so why is it distracting?<br />
<br />Anonymoushttp://www.blogger.com/profile/04441790807840642185noreply@blogger.com0tag:blogger.com,1999:blog-4657142000183363795.post-22715603778003954012013-11-22T16:19:00.001-08:002013-11-22T16:19:07.913-08:00Extra DimensionsMusic AND art day today. Oh my.<br />
<br />
What I learned today: My non-artist brain likes to think of hair as strands. This is not at all helpful when trying to make a rough pencil sketch, where you really just want to trace the outline and overlaps. <br />
<br />
Most of the day was spent composing "Hyperspace," a high energy little tune for those times when you are hurtling though extra-dimensional space with reckless abandon and trying not to die in a giant fireball from an asteroid collision. Seriously, we've all been there.<br />
<br />
It's coming along. I have a few of the basic bits worked out, though there's quite a bit to go, and I'm not "feeling the love" yet. The main problem with trying to compose a specific song for a specific purpose is that if it's not working, you're still kind of committed. I start a lot of pieces, but only a handful ever get finished because they'll have one cool riff that just doesn't go anywhere. We'll see how it goes, hopefully I don't have to reboot it. :)<br />
<br />
The other thing of note is that my giant snare from yesterday is probably a bit airy for something that wouldn't sound too out of place coming from Symphony X or Metallica, so that might need some work. We'll save that for some heavy, plodding, real-space themed song. :)<br />
<br />Anonymoushttp://www.blogger.com/profile/04441790807840642185noreply@blogger.com0tag:blogger.com,1999:blog-4657142000183363795.post-72008414494315468552013-11-21T20:37:00.000-08:002013-11-21T20:37:15.048-08:00Heavy Metal GamesIt's a music day today.<br />
<br />
I spent most of the day figuring out how to create a decent heavy metal drum kit. I think I'm basically happy with the basic sound now. The toms could maybe use some work, but the kick and snare are sounding decent.<br />
<br />
So, things learned today:<br />
<br />The kick drum doesn't really need much of its low frequency range. EQing up anything below 75Hz just muddies things up, though you do need a bit of low end. The interesting parts of the kick actually occur closer to 4Hz and 8KHz, where the "click" actually happens and makes the kick punch through the mix.<br />
<br />
To make the giant 80s snare sound basically calls for a gated reverb. That's basically an effect chain with a reverb followed by a noise gate. To get the noise gate to work is a bit of a trick, because you want it keyed off the initial sound level, not the reverb level. I'm using <a href="http://reaper.fm/">Reaper</a>, which has a fairly easy method of doing this:<br />
<ol>
<li>Send the snare to a separate track for just the snare reverb</li>
<li>Give that track 4 channels</li>
<li>Effect chain: </li>
<ul>
<li>Duplicate the input channels 1/2 to channels 3/4 (Utility/chanmix2)</li>
<li>Add the reverb</li>
<li>Add the gate (ReaGate)</li>
<ul>
<li>Set the Detector input to Auxiliary Input </li>
</ul>
</ul>
</ol>
<br />
The main trick here is that ReaGate lets you use a channel for volume detection that is separate from the main signal. There are probably other methods in other recording software, but this works nicely here.<br />
<br />
Then I did a little playing with parallel compression, which is just mixing the original uncompressed sound with a heavily compressed version of it. I still need to look into this some more.<br />
<br />
This took a lot of tweaking, and then recording a guitar and bass loop so I could see how they sounded mixed together. It's quite interesting how things can sound vastly different (for better or for worse) one you actually mix them all together.<br />
<br />
So, tomorrow will hopefully be composing a test piece for a level and maybe actually getting object spawning synced with it.<br />
<br />Anonymoushttp://www.blogger.com/profile/04441790807840642185noreply@blogger.com0tag:blogger.com,1999:blog-4657142000183363795.post-87543121547045571652013-11-20T18:58:00.000-08:002013-11-20T18:58:05.680-08:00Title Goes HereSometimes, figuring out a clever title for the blog post is the hardest part. Yesterday's drawing failure, power failure and failure to fail made for a fairly obvious choice though. :)<br />
<br />
Then there are days like today that just devolve into a bug hunt and "oh I didn't implement that yet did I" moments.<br />
<br />
I can at least sequence object spawning from a script now.<br />
<br />
Other things on today's menu included trying to coax a good heavy metal drum kit out of Native Instruments' Studio Drummer. I had a kit set up with another plugin that I really liked, but it tends to lose its settings and otherwise behave badly. The drums in Studio Drummer sound really, really nice, but they're a little subdued for what I was going for. I'm sure it can be fixed with the proper application of EQ, compression and reverb, but it's going to take some work.<br />
<br />
So, now that I'm able to spawn an object, I discovered... I really need to figure out my enemy design. :) I'm trying to figure out a basic "building block" piece that can be duplicated assembled into multiple different structures. I haven't quite figured out exactly what that shape is yet.<br />
<br />Anonymoushttp://www.blogger.com/profile/04441790807840642185noreply@blogger.com0tag:blogger.com,1999:blog-4657142000183363795.post-77728338591184715142013-11-19T21:31:00.002-08:002013-11-19T23:56:56.063-08:00Fail, Fail, FailOK, busy day. Productive, maybe not so much, but busy.<br />
<br />
First, I was playing around in GIMP trying to figure out why I Can't Draw. Today's discovery: I can draw horizontal lines relatively well. Just drawing over the same horizontal line has a very tolerable margin of error. I can't draw vertical lines <b>at all</b>. They're not straight, and trying to draw over the same line has like 1000% error. Minimum. Tracing circles is interesting too. There are certain quadrants that I can trace very accurately. Then I get to, primarily, the lower right quadrant and my pen wobbles all over the place, no matter how slowly or careful I'm trying to be. This requires more study, because I can't yet detect the subtlety that is causing thing to go wrong.<br />
<br />
Then, the wind knocked the power out. Not helping. Then I even briefly lost contact with the cell phone network. Seriously, I nearly started sharpening sticks to go hunt wooly mammoths during this brush with the stone age.<br />
<br />
Fortunately this brush with abject terror only lasted about 90 minutes, but the UPS couldn't handle it and my Linux uptime is ruined.<br />
<br />
<br />
Next, time to do some actual work. :) I started working on some language features to make object spawn scripting work better. Primarily this involved adding some coroutine support so I can make a timed event wait for that time to arrive. The system works more or less fine for sequencing things without this, but it's hard to get things to trigger only once.<br />
<br />
I'm still debating how I want to do certain things. On one hand, it would be nice to spawn a specific object at a specific place at a specific time with specific AI. On the other, pseudo-randomly generating an entire level has a certain appeal. Unfortunately, the latter has had me stalled for much too long and it's time to press on.<br />
<br />
The idea was for this first game to fail quickly while I built up technology, so I could make the second a success. It's not failing quickly enough... or, something.<br />
<br />Anonymoushttp://www.blogger.com/profile/04441790807840642185noreply@blogger.com0tag:blogger.com,1999:blog-4657142000183363795.post-85645721369909503072013-11-18T15:39:00.002-08:002013-11-18T15:39:53.981-08:00Always One More Thing. Or Two.Last weeks daily updates were not exactly daily (and therefore, I suppose, not updates either). Let's see if this week goes better. :) <br />
<br />
At least you got to miss all of the updates that can be roughly summarized as: "I don't feel well today, YouTube, brain fog, Twitter, I can't figure this out, Facebook, what was I doing again? twitch.tv, this is never going to work, oh I got something working when did that happen?"<br />
<br />
I got most of the major overhaul of the scripting system done, so I can basically map any C object directly into the scripting system. I'm sure that's giving some security expert somewhere palpitations or convulsions, but since it's all bound to specifically named objects and data members, it should be fine.<br />
<br />
Overall, this is actually kind of handy, although I'm still not "feeling the love" for a good way to define level scripts. Somehow, writing out every enemy spawn, by hand, at specific locations, just feels a bit meh. A quick perusal around the web of methods other people have used pretty much comes down to exactly that, though.<br />
<br />
So, I suppose it's time to actually do some real scripting. But first, I need to... make a couple more tweaks to the scripting system (you didn't see that coming, did you? :-p )<br />
<br />
No, really, they're good ones.<br />
<br />
First off, I'm kind of trying to sync the levels to music, so I'm going to add something to natively convert measure/beat time codes to seconds. Easy enough.<br />
<br />
Second, one of the main suggestions I have run across regarding scripting enemy spawns is that hard-coding the numbers is bad because if you want to shift sections around, you have to change a lot of numbers. So I'm going to build in the concept of time regions. Basically, there is a global time based on how long the level has been running, and you can recursively open a new local time scope relative to the parent time. I've used this before, it's pretty slick for sequencing or animating things. It should make it fairly easy to move around sections anyway.<br />
<br />
With those things and a Spawn() function, hopefully I'm off and running.<br />
<br />
Why it took so long to get to this point is one of the great mysteries of the universe. I suspect it has something to do with the universal programming equation:<br />
<br />
t = t * 2<br />
where:<br />
t is how long you estimate something is going to take and<br />
2 is some constant, possibly larger than -INF and smaller and +INF, but quite possibly NAN.<br />
<br />
Being recursive, it causes some consternation amongst project managers and people trying to get things done.<br />
<br />Anonymoushttp://www.blogger.com/profile/04441790807840642185noreply@blogger.com0tag:blogger.com,1999:blog-4657142000183363795.post-65079869840211158582013-11-13T14:00:00.000-08:002013-11-13T18:17:16.802-08:00The Code That BindsI have been spending the last little while trying to get the data binding stuff worked out.<br />
<br />
After several false starts, I have finally settled on using the expression parser / compiler to do the data binding. Ultimately, this seemed to make the most sense. Copying data from one arbitrary place to another is basically what a script is supposed to do, plus it gives some additional flexibility.<br />
<br />
Quite often it seems that the best approach to solving problems is to ask, "how do I want to interact with this system" rather than, "how do I want to implement it?" Basically, I decided I wanted to specify data binding something like this:<br />
<br />
<blockquote class="tr_bq">
<span style="font-family: "Courier New",Courier,monospace;"> ScriptComponent<br /> {<br /> Context<br /> {<br /> src = BezierFunctionComponent<br /> dst = SpawnComponent<br /> }<br /> Script = dst.Rate = src.Value(t)<br /> }</span></blockquote>
That basically lets me map specific components to variable names in the script environment, and then operate on them. Since this all gets compiled at load time, and the above example only generates a tiny number of nodes in the parse tree (say, 4 or 5), this should execute insanely fast and be very flexible.<br />
<br />
Unfortunately, the variable system I had in place was somewhat limited. So, I'm about half way through getting a proper symbol table system implemented. This basically lets me memory-map a component into the script system, with the added benefit of putting structure definitions and stack frames (local variables) into the scripting system in general.<br />
<br />
Still a ways to go, but making progress.<br />
<br />Anonymoushttp://www.blogger.com/profile/04441790807840642185noreply@blogger.com0