Yes, it's been way too long since anything was posted on the blog, but the amount of blog activity is generally inversely proportional to how intensely I'm working on the game!
I'm pleased to report that a closed alpha testing session is currently taking place, which should help iron out any system-related issues. Things are looking good so far for the main game engine being stable over a variety of hardware configurations.
After a few iterations of testing and improvement there will be a general alpha release posted on this blog, so watch this space!
Brainworm Blog
This is the blog of Brainworm Software, which currently concentrates on the development of the indie game Juggernaut.
Sunday, 30 October 2011
Sunday, 14 August 2011
Back from holidays!
The last month has been a little slow on development as I've had a couple of weeks of holiday, leaving me feeling refreshed and possibly even a little tanned.
The most important recent update is that scripted animated objects are now up and running properly. This means that doors (and any other environment object) may be animated by script strings, meaning that proper game interaction is now possible to generate, such as the player's craft needing to move to a specific location within the world and activate an object (which then executes the script) in order to open the door. These scripted objects are properly integrated into the physics engine, such that they impart appropriate force to any dynamic objects they collide with during their animations.
Now, the primary focus is on further development of the AI algorithms for the swarm, such that they can spot the player's ship and attack appropriately, including chasing them through the environment and performing proper route-finding. The basic navigation node structure is already complete and searchable, the main weakness is the lowest-level control i.e. correctly navigating between nodes without going out of bounds and colliding with the world.
I'm currently holding off creating a new video for a while until there are enough visible differences to make the recent updates apparent. Also, I had it pointed out to me recently that in all the videos so far I've been playing the game really badly (purposefully), mainly to show off collision particle effects etc., but I'll make sure on the next one to actually fly around normally!
The most important recent update is that scripted animated objects are now up and running properly. This means that doors (and any other environment object) may be animated by script strings, meaning that proper game interaction is now possible to generate, such as the player's craft needing to move to a specific location within the world and activate an object (which then executes the script) in order to open the door. These scripted objects are properly integrated into the physics engine, such that they impart appropriate force to any dynamic objects they collide with during their animations.
Now, the primary focus is on further development of the AI algorithms for the swarm, such that they can spot the player's ship and attack appropriately, including chasing them through the environment and performing proper route-finding. The basic navigation node structure is already complete and searchable, the main weakness is the lowest-level control i.e. correctly navigating between nodes without going out of bounds and colliding with the world.
I'm currently holding off creating a new video for a while until there are enough visible differences to make the recent updates apparent. Also, I had it pointed out to me recently that in all the videos so far I've been playing the game really badly (purposefully), mainly to show off collision particle effects etc., but I'll make sure on the next one to actually fly around normally!
Sunday, 10 July 2011
Token post!
OK, I say I'll do more regular updates and then immediately fail to post anything for a week. Welcome to OppositeLand, population me.
Work is still ongoing, worry ye not, with the focus on improving the scripting system and I/O for the more complex objects within the world map. Also, within the main asset management system there's now an overall object dictionary that handles everything from physics objects to particle fountains, allowing them to be spawned easily in a unified way.
So yes, at the moment it's all core engine work, but this is all necessary (except for a few sidetracks) in order to achieve my current main goal of scriptable game objects.
Work is still ongoing, worry ye not, with the focus on improving the scripting system and I/O for the more complex objects within the world map. Also, within the main asset management system there's now an overall object dictionary that handles everything from physics objects to particle fountains, allowing them to be spawned easily in a unified way.
So yes, at the moment it's all core engine work, but this is all necessary (except for a few sidetracks) in order to achieve my current main goal of scriptable game objects.
Thursday, 30 June 2011
The start of a series of shorter updates.
Up until now I've tended to try and get out one decent-length blog post a week during development, but I think I may change that pattern to trying to post something short today to give a quick snapshot of what I'm working on at any given point.
The main thing I'm up to at the moment is working on the world scripting - my primary goal is to create an animated door that can open/close based on interaction with a world object. I've already gotten the main scripting up and running, such that interacting with a destroyed ship will update the amount of resources you have, give you a new ship component or start a given bit of narrative, but this push towards having a properly dynamic environment will make a lot of difference.
The custom scripting language and the asset management system are tightly integrated. Here's an example of the current scripting that is implemented and working:
"$Timer #3.0 $ZoneResource1 #OFF"
The $ prefix is used to reference an asset that exists in the global asset management framework, and the # is used to indicate a constant value. In this case, there is a special asset called 'Timer' that allows scripts to be called with a specific delay; the #3.0 indicates that the delay should be 3 seconds, and the rest of the parameters '$ZoneResource1 #OFF' are the script to execute once the timer period has completed. ZoneResource1 is another interaction zone, and the #OFF value indicates that it should be rendered inactive after the call. If the last two parameters were replaced by 'script:ExampleScript', the asset management system would check to see if the ExampleScript asset were already in memory and, if not, load it in from a specific default directory.
[Foreseeing potential questions on this issue]
Why didn't I use Python? Because BOLLOCKS, that's why.
Of course, any time you try to do X in development, you tend to find yourself doing Y, Z and occasionally α to support getting X to work, as well as tidying up other bits that you come across while doing Y and Z. As a result, the damage callback system has been given a bit of an overhaul in the last day or so.
Right now, I'm working on an object factory so that a wide variety of different object types may be simply loaded, allowing enemy spawners, physics objects, debris, animated objects etc. to be loaded by the same framework. This will then mean that dynamic components (such as the opening/closing door) will be automatically created and assigned to the right list, and then may be scripted (after a wee bit of work on animation).
[Edit: OK, so that didn't turn out to be so short].
The main thing I'm up to at the moment is working on the world scripting - my primary goal is to create an animated door that can open/close based on interaction with a world object. I've already gotten the main scripting up and running, such that interacting with a destroyed ship will update the amount of resources you have, give you a new ship component or start a given bit of narrative, but this push towards having a properly dynamic environment will make a lot of difference.
The custom scripting language and the asset management system are tightly integrated. Here's an example of the current scripting that is implemented and working:
"$Timer #3.0 $ZoneResource1 #OFF"
The $ prefix is used to reference an asset that exists in the global asset management framework, and the # is used to indicate a constant value. In this case, there is a special asset called 'Timer' that allows scripts to be called with a specific delay; the #3.0 indicates that the delay should be 3 seconds, and the rest of the parameters '$ZoneResource1 #OFF' are the script to execute once the timer period has completed. ZoneResource1 is another interaction zone, and the #OFF value indicates that it should be rendered inactive after the call. If the last two parameters were replaced by 'script:ExampleScript', the asset management system would check to see if the ExampleScript asset were already in memory and, if not, load it in from a specific default directory.
[Foreseeing potential questions on this issue]
Why didn't I use Python? Because BOLLOCKS, that's why.
Of course, any time you try to do X in development, you tend to find yourself doing Y, Z and occasionally α to support getting X to work, as well as tidying up other bits that you come across while doing Y and Z. As a result, the damage callback system has been given a bit of an overhaul in the last day or so.
Right now, I'm working on an object factory so that a wide variety of different object types may be simply loaded, allowing enemy spawners, physics objects, debris, animated objects etc. to be loaded by the same framework. This will then mean that dynamic components (such as the opening/closing door) will be automatically created and assigned to the right list, and then may be scripted (after a wee bit of work on animation).
[Edit: OK, so that didn't turn out to be so short].
Tuesday, 14 June 2011
Because banging can be fun, can't it?
OK, the title is a reference to this video (RIP Roy Skelton), which is marginally justified due to this post including physical collisions between convex objects.
Since the last post quite a few significant updates have been made, which are best expressed through the medium of bullet points.
Since the last post quite a few significant updates have been made, which are best expressed through the medium of bullet points.
- The convex object collision detection/physics is working well (huzzah!)
- Springs (currently massless) have been implemented in the physics engine, which will form the basis of the tractor beam with Juggernaut.
- The damage and destruction framework has been significantly upgraded to include penetration levels for both laser and projectile weapons. For example, in the video you will see that the laser weapon immediately terminates upon hitting the debris, but passes through several Scourge enemies before terminating. A larger penetration factor for a weapon will improve its effectiveness against multiple enemies, but not against single tougher enemies.
- Each object can now have a convex collision shape associated with it that will be intersected with by laser + arc weaponry. This replaces the bounding-circle based object/object collision.
- Upgrades the asset management framework that allows resource files containing multiple asset types to be loaded in transparently. Dull, but very handy ;)
Sunday, 5 June 2011
Environmental decoration: instanced cilia
As well as all the ongoing work on the physics engine (the convex polygon routines now work except in a couple of extreme cases that I'm fixing), I've taken a little time out to make things more pretty. In this case, I've added some animated 'cilia' that will be used to add dynamic motion to some of the infested areas. The initial version of this is shown in the video below, although I've already updated the actual graphics to add a small bright tip to each strand, stopping it from just looking like grass.
Although there may be thousands of cilia on-screen at any one time, all the work is done by the GPU using immutable buffers. A specialised vertex shader adds the sinusoidal animation based on a time value supplied via a shader variable, as well as adding a small size variation. Also, rather than manually defining the position/angle of each cilia in a level (which would be quite memory-hungry), areas of cilia are just defined by a straight line with a normal.
To further improve the effect, cilia further away from the camera have their colour dimmed in order to provide a depth cue.
Although there may be thousands of cilia on-screen at any one time, all the work is done by the GPU using immutable buffers. A specialised vertex shader adds the sinusoidal animation based on a time value supplied via a shader variable, as well as adding a small size variation. Also, rather than manually defining the position/angle of each cilia in a level (which would be quite memory-hungry), areas of cilia are just defined by a straight line with a normal.
To further improve the effect, cilia further away from the camera have their colour dimmed in order to provide a depth cue.
Sunday, 29 May 2011
Stable 2D Contact Points between Convex Objects
The purpose of this post is to describe a bit about how the 2D collision detection works in Juggernaut. Some of it may be overly basic, and some bits may skip massively over other aspects, but it does cover a few points (particularly on convex object collision detection) that I didn't find directly anywhere else on the web, and should at least provide a good overview.
In order to integrate a shape into the physics engine, you need to be able to detection intersections between that shape and the others supported by the collision detection engine.
A contact point is defined by the intersection point between the two objects, the collision normal, and the penetration depth. The circle/circle case is the simplest, as you just take the vector between the two circle centres and compare the magnitude to the sum of the radii.
For the line/circle case you need to create the function closestPointOnLine( Point A, Line B ), which will determine the closest point on the line segment B to the point A, which may either be a point along B or one of the endpoints of B. Intersection/non-intersection may then be determined by comparison of the distance of the circle centre to the closest point on B with the radius of the circle.
For a more in-depth look (including the actual maths) this site is a very useful resource.
The key concepts and algorithms required in order to solve this problem are:
- The Minkowski difference (see here).
- The Gilbert–Johnson–Keerthi distance algorithm (or just GJK algorithm)
- Alternatively to GJK, a Separating Axis Theorem-based approach such as the Lin-Canny algorithm.
- The Expanding Polytope Algorithm, which may be used in conjunction with GJK in order to improve performance on deep penetrations.
The Juggernaut convex shape handler is based around GJK, and I'll mention a couple of things that I've found about it.
So, the solution that I've come up with is as follows: for the shape where only one vertex is returned by GJK, check both edges that are connected to it for contact points. This is almost certainly not the only solution to this problem, but it's the only one I've found that produces stable results thus far. This is demonstrated in the diagram below:
In order to integrate a shape into the physics engine, you need to be able to detection intersections between that shape and the others supported by the collision detection engine.
Simple Primitive Intersection
Up until now, Juggernaut has only properly supported circle/circle and circle/line interactions: the main world brush is defined by a series of straight lines (organised into a hierarchical tree structure for efficient collision), and the objects themselves are defined by a series of bounding circles. These are the simplest types of collision, as there is only ever one possible contact point between the primitives (to clarify, circles can have two actual intersections with a line/other circle, but only a single contact point is required to resolve them).A very rough image showing example contact points/normals for circle/line and circle/circle collision detection. By convention, the normals are defined as pointing into object A. |
For the line/circle case you need to create the function closestPointOnLine( Point A, Line B ), which will determine the closest point on the line segment B to the point A, which may either be a point along B or one of the endpoints of B. Intersection/non-intersection may then be determined by comparison of the distance of the circle centre to the closest point on B with the radius of the circle.
For a more in-depth look (including the actual maths) this site is a very useful resource.
Convex Primitive Intersection
So, for the simple primitive case, the contact point information can be calculated directly. However, what if we want to have a more complex shape, such as an arbitrary convex polygon? Convex polygons are selected as dealing directly with non-convex polygons is much more difficult, and it is always possible to decompose non-convex polygons into multiple convex polygons. In this case, working out whether the objects intersect is much more difficult, and a naive approach would probably involve comparing every edge in shape A with every edge in shape B. Luckily, this is not necessary!The key concepts and algorithms required in order to solve this problem are:
- The Minkowski difference (see here).
- The Gilbert–Johnson–Keerthi distance algorithm (or just GJK algorithm)
- Alternatively to GJK, a Separating Axis Theorem-based approach such as the Lin-Canny algorithm.
- The Expanding Polytope Algorithm, which may be used in conjunction with GJK in order to improve performance on deep penetrations.
The Juggernaut convex shape handler is based around GJK, and I'll mention a couple of things that I've found about it.
- It works absolutely amazingly for determining whether objects intersect or not.
- If the objects *do* intersect, the raw GJK results can be unreliable, as the closest simplex edge won't necessarily correspond to an edge on the convex hull of the Minkowski difference. To ensure that this always happens, you'll need to use the Expanding Polytope Algorithm in these cases for robust behaviour.
- Both Shape A vertices are identical and the Shape B vertices define an outside edge of B.
- Both Shape B vertices are identical and the Shape A vertices define an outside edge of A.
- Both the Shape A and Shape B vertices define outside edges of A and B, respectively. This will only really occur when the edges are very close to parallel, and can also be associated with a triangular simplex of zero are. Also, this may or may not occur in exactly the same situation depending on the initially selected simplex point.
- The vertices do something else and define non-external edges of A and/or B (may happen with raw GJK, but not GJK+EPA).
So, the solution that I've come up with is as follows: for the shape where only one vertex is returned by GJK, check both edges that are connected to it for contact points. This is almost certainly not the only solution to this problem, but it's the only one I've found that produces stable results thus far. This is demonstrated in the diagram below:
Subscribe to:
Posts (Atom)