Jump to content

Flexman

Members
  • Posts

    916
  • Joined

  • Last visited

Blog Entries posted by Flexman

  1. Flexman
    About: This is a short series for the Leadwerks community on the process of creating a simple game using procedural content.
     
    This week we implement the map creation process discussed in part 02 then add the mesh generation and a controller to fly around our level. Then we'll have the first iteration of our procedurally generated map.
     
    Part 01
    Part 02
     
     
     
    Starting with the Map class.
     
    Map:Create Function...first iteration, no corridors.
     
    Simple nested for-loop to generate a room for each cell. Each room is stored in the table Map.Room[] with dimensions, a counter for reference and a cell offset in world units.
     
    For our example a cell is a virtual 40x40 space, our example map is made of 4 x 4 cells.
     
    We call the function like this...

    Map:Create (407, 4 , 4 , 40 )
     
    And our function definition is...
     

    function Map:Create( seed , xsize , ysize , cellscale , roomscale ) self.xsize = xsize self.ysize = ysize self.seed = seed self.roomscale = roomscale math.randomseed( seed ) if roomscale == nil then roomscale = 1.0 end Map.roomscale = roomscale if cellscale == nil then cellscale = 40.0 end Map.cellscale = cellscale Map.roomheight = 2.0 Print("Creating map, dimensions " .. xsize .. " x " .. ysize) -- table to store our rooms Map.Room = {} -- room counter rcount = 1 for x=1,xsize do for y=1,ysize do if math.random() > 0.2 then self.Room[rcount] = {} local r = self.Room[rcount] r.roomID = rcount r.cellx = x r.celly = y r.x = math.random(cellscale) r.y = math.random(cellscale) r.width = math.ceil( math.random(3 , cellscale ) ) r.length = math.ceil( math.random(3 , cellscale ) ) r.info = string.format("id %.2d cellx %.2d celly %.2d x:%.3d y:%.3d w:%d h:%d", r.roomID , r.cellx , r.celly , r.x , r.y , r.width , r.length ) print(r.info) rcount = rcount+1 end end end self:MakeGeometry() end
     
    In the above code to reduce rooms arranged in a solid 'grid' there's a random chance that a cell skips room creation. The cell offset ( r.x and r.y ) serves to add more irregularity to the layout.
     
    Map.roomscale and Map.cellscale: bigger number = bigger space. One scales distance between rooms, the other scales the room mesh.
     
     
    After generating the geometry from such an arrangement we get this...

     
    I've added a head-up display to show an overhead map and annotations to the screenshot to show how the distribution of rooms work. So far so good.
     
    Perhaps worth mentioning the CELLS are ordered top to bottom then left to right. That gives you some idea that the random offset works to radically shift rooms around to avoid being too attached to the grid arrangement.
     

    Info: CELL is a term I use to describe a container in virtual space.  
    Before we generate any geometry for our rooms we have all the data we need to display a map (like the one above). Debug overlays or HUDs are handy during early development and can be migrated to a finished game HUD later. All we need right now is some way to check room volumes and positioning, some info and the players position. Later we can use a second camera to draw a top-down view if required.
     
    Another feature we want in our HUD is some flag to draw it, a position to move it about the screen and a scale so we can fit the map to the whole screen or just squeeze it into a corner. The following code does all this. X and Y is typically used as a screen-coordinate position to draw an element and s is the size in pixels to fit the map into.
     
    If you need to re-size text as well then you need to start rendering these to a buffer but we'll avoid the extra complexity. This is often made easier with OpenGL commands but they are not exposed to Leadwerks LUA scripts (as of 2.5).
     
    Drawing the HUD and overhead map
     

    function DrawHUD() if App.showhud ~= true then return end SetBlend(2) SetColorf(0.1,0.3,0.1,0.3) DrawText("ROOM DATA",22,22) local c = string.format("RoomCount %d", #Map.Room) DrawText(c,22,60) local x = 40 local y = 80 for n,r in pairs(Map.Room) do DrawText( r.info , x , y ) y = y + 16 end x = 400 y = 80 s = 512 sx = s / (Map.cellscale * Map.xsize) sy = s / (Map.cellscale * Map.ysize) DrawText("OVERHEAD MAP",x,y-20) --DrawLine(x,y,x+s,y) DrawLine(x+s,y,x+s,y+s) DrawLine(x+s,y+s,x,y+s) DrawLine(x,y+s,x,y) for n=0,Map.xsize do DrawLine(x + (n*Map.cellscale*sx) , y , x + (n*Map.cellscale*sx) , y + s) end for n=0,Map.ysize do DrawLine(x , y + (n*Map.cellscale*sy) , x + s , y + (n*Map.cellscale*sy)) end -- sorry for the fiddly math here -- its needed to match the map scale with the HUD display scale for n,r in pairs(Map.Room) do local roomx = x + (( r.x + (r.cellx-1)*Map.cellscale) *sx ) local roomy = y + (( r.y + (r.celly-1)*Map.cellscale) *sy ) DrawRect( roomx , roomy , r.width * sx, r.length * sy ) DrawText(string.format("%.2d",r.roomID), roomx + ((r.width*sx)*0.5)-8 , roomy + ((r.length*sx)*0.5)-9 ) end DrawText(string.format("view co-ords x:%d y:%d", camera.position.x , camera.position.z) , 40, GraphicsHeight() - 20 ) -- player marker cross in yellow SetColorf(0.3,0.3,0.1,0.3) DrawText("X", x + (camera.position.x * sx)-4 , y - (camera.position.z * sy) -8 ) SetBlend(0) end
     
    The map size is adjusted by changing "s = 512" to however many pixels across. Objects are scaled and drawn accordingly. Once we start merging rooms this will need some alteration, the overhead camera might be a viable alternative and one we can have some fun with.
     

    randomly flagged tile data, looks like a "BallBlazer" level.
     
     
    Tiles and Geometry
     
    Rather than write up how it works you can look through the code and tinker with it. The next and penultimate part will cover generating corridors and using "tile" data to merge overlapping rooms and add props like doors. You'll see already there are random light sources assigned to each room, random assortments of props such as columns, crates, particles etc. can be done in a similar fashion.
     
     
     
    The Code for this article
     
    I've attached the full code which creates the room data and geometry (sans no corridors and intelligent tile merging - we'll deal with that in part 04).
     
    Don't forget to execute this script using ScriptEditor.exe (or run it with ENGINE.EXE) you will need to make sure the path at the top of START.LUA (MediaDir) points to your Leadwerks SDK location and the default scripts are present (as "required" at the top of the script).
     
    You'll find a few bits of code commented out and older functions I used to create meshes. I've left them in for curiosity (learning and laughter).
     
    Until part 04, have a good weekend.
  2. Flexman
    About: This is a short series for the Leadwerks community on the process of creating a simple game using procedural content.
     
    In part 01 we looked at one theory of generating random maps in the style of old school dungeon crawlers. This week we'll put that into practice to generate map data, room geometry and mesh colliders. The easy part was adding a flying physics based camera to act as our first-person flown "spaceship".
     
     
    Again I want to flag I'm not a LUA expert, this is not a LUA tutorial but like most languages they are a syntax away from common functionality. It includes examples and explanations of some LUA code to relate why I did something a certain way. What you see is what you get. All code is freely available for use and modification however you see fit.
     
     

    How to use the code and run any examples in this series
     
    After working on the code a short time I found using the Leadwerks ScriptEditor.exe better for the process rather than attempting to run and debug it as an editor script. And one last note before we get on with it, 99% of the assets are included the Leadwerks SDK media folder so please edit the "MediaDir" in the LUA source. Any other required media will be included as a download.
     
    Pre-amble over with. Lets get to it.  
     
    Map Data to 3D Geometry - first experiment
     
    The focus for this tutorial is generating map data then creating 3D geometry from it. The latter raised several questions about what would work for a tutorial and flexibility for use in different kinds of games. So I've focused more on this side for part 02. (Also I had to re-write it meaning less time to work on the map data code).
     
    My first room generator working prototype created "shoe boxes" as pictured below. With lighting, ambient sounds and the physics on a controller to fly your 'ship' around in first-person view it was a passable bit of code.
     
     


    figure 2.2 - "ShoeBox" room and corridor structure  
    "ShoeBox" was efficient in that it was a collection of flipped volumes with light sources, individually painted surfaces (6 materials) and would be good for a simple iPhone adventure.
     
    When it came to merging volumes for more complex rooms I realised there would be a lot of code needed to split up geometry and re-build it. Rather than turn it into an exploration of CSG, triangulation and mesh simplification (which is not trivial) I ditched it for another simpler approach.
     
    Going back to how 2D games work, room geometry became a collection of grids (figure 2.2) that can be thought of in terms of traditional 'tiles'.
     


    figure 2.2 - tiled room (aka a regular mesh)  
    Looking more like a terrain mesh with walls and roof, the grid structure makes it easier to merge volumes, add doors, corridors and holes traps or vertical shafts. And by increasing the mesh density and adding vertex noise you can turn rooms into caves with bumpy walls. I felt justified in presenting this method as it allows more possibilities and simplifies geometry construction.
     
    In appearance it's quite like old-school cells and tiles, similar to existing 2D dungeon map generators. In my working prototype you can't individually paint a tile but it might be possible to modify the code to allow for UV painting and save out this data. Each room can have it's own unique set of materials.
     
     
    LUA code-time. Geometry Construction
     
    Leadwerks like other engines provides tools to create solid shapes entirely in code. First you need to create a Mesh object then a Surface object attached to that Mesh.
     

    local room = {} room.mesh = CreateMesh() room.surface = CreateSurface(room.mesh)
     
    Note: For clarity this is using ONE surface, this will be expanded to use a six surfaces for each side of the room. The important part is that you add geometry to surfaces, you paint surfaces with materials, you create colliders with surfaces. It's all about the surfaces baby.
     
    All room elements such as walls, floor, lights, props etc. are parented to the room.mesh which is positioned in world space. Moving the room is as simple as calling Map:Room[id].SetPosition(x,y,z)
     
    Really evil dungeon masters might want to re-position a room with players inside. Well you can do that, even rotate it. Parent the room to the whole map (which has a pivot) and you can roll the entire map around for a marble game. All walls are solid but as they are one sided you can see right through them when viewing from outside.
     
     
    Patch Maker
     
    Let's look at a LUA code snippet for creating the roof and floor mesh, I want to draw attention to one of the cute things LUA can do and if you've not come across it before it can be confusing. So I draw your attention to it now. The assignment of (nameless) functions to a table and indexing them by string.
     
    Depending on what part of the room we are building we take care vertices are wound the correct way and the vertex normals are perpendicular. So I put the AddVertex() and AddTriangle() calls into a table and for ease of identification indexed them by string such as "roof", "floor", "north_wall" (walls not included here for clarity).
     

    addvert["floor"] = function (s,x,y,height,xsize,ysize) return s:AddVertex ( Vec3( x-(xsize * 0.5), height , y-(ysize *0.5) ) , Vec3( 0 , 1 , 0 ) , Vec2(x / (ysize*0.1), 1.0- y/(xsize*0.1)) ) end addvert["roof"] = function (s,x,y,height,xsize,ysize) return s:AddVertex ( Vec3( x-(xsize * 0.5), height , y-(ysize *0.5) ) , Vec3( 0 , -1 , 0 ) , Vec2(x / (ysize*0.1), 1.0- y/(xsize*0.1)) ) end addtris["floor"] = function (s,v,ysize) s:AddTriangle ( v-ysize-1, v , v-1 ) s:AddTriangle ( v-ysize-2 , v-ysize-1 , v-1 ) end addtris["roof"] = function (s,v,ysize) s:AddTriangle ( v-1, v , v-ysize-1 ) s:AddTriangle ( v-1 , v-ysize-1 , v-ysize-2 ) end function Map:CreatePatch( surface , width , length , height , mode ) for x=0,width do for y=0,length do vcount = addvert[mode](surface,x,y,height,length,width) -- we degenerate first row and column when making a grid if ((y>0) and (x>0)) then addtris[mode](surface, vcount , length) end end end end
     
    Our room geometry code needs to call the patch builder with dimensions and the name of the component like thus...
     

    -- example useage from the room builder Map:CreatePatch(room.surface, 8 , 8 , 0 , "floor") Map:CreatePatch(room.surface, 8 , 8 , 2 , "roof")
     
    That's the easy part. We will need to extend Map:CreatePatch to take our merged room data and generate an appropriate mesh to fit the irregular shape of our rooms and corridors as supplied by our dungeon creator.
     
    Now we have a method to generate basic rooms which is easy to modify we can look at the other problem; creating our random map.
     
    Remember the map structure we're looking for, a grid of cells into which we position at random offsets a regular set of rooms (see figure below). Each room being of variable size and can overlap. For this part of the tutorial we're going to keep the rooms small enough to prevent overlap to keep the source size down and reduce the complexity. We'll cover triangulation for a more advanced geometry function in a later part. In other words I couldn't get it to work properly in the time allotted.
     


    figure 2.3 - room and corridor structure  
     
    We'll begin with a creator function...
     

    -- function to build entire map -- params: random seed value, xsize and ysize (dimensions of map in cells) -- roomscale (is multiplier of room size in world units) function Map:Create( seed , xsize , ysize , roomscale ) math.randomseed( seed ) if roomscale == nil then roomscale = 1.0 end Map.roomscale = roomscale Map.roomheight = 2.0 Print("Creating map, dimensions " .. xsize .. " x " .. ysize) -- table to store our rooms Map.Rooms = {} ... ... end
     
    For our dynamically generated game level, we need to know a couple of things;
    How big in game cells (how many rooms across and down)
    The seed from which the random nature of all springs forth (how poetic)
    How big we want the rooms to be relative to each game cell (room scale).

    A tile is simply a value stored in a 2D array e.g. tile[x,y]
     
    Our data would benefit from being organised into a grid. A 2D tile structure which represents an overhead view of our 3D world. This makes tile arrangement makes it simple to merge rooms and corridors, flag as walls, doors, traps and anything else we might want to generate. However...(c'mon you knew that was coming)...
     
    If we want to make really HUGE levels then array mapping will be inefficient, the 400x400 grid in our example will be fine but anything larger will become resource hungry as we increase the world size. Much of it becomes empty space.
     
    To avoid unnecessary memory usage and run-time costs we will virtualise the space a little. We've done it already with our room nodes (figure 2.4)
     
     


    figure 2.4 - room nodes are virtualised rooms  
    Note: A room object simply exists at world_x, world_y and is this >< big. This is for boundary test and positioning.
     
    We will use a 2D array of tiles for each room. Each tile maps to a room_x, room_y. This way the data is available to game logic for locating objects, spawn points.
     
    After room generation is complete we run a post processing function to merge rooms, this is where the tile array comes in handy.
     
    The room merge steps through all rooms, a boundary check is performed on neighbours and merging into a new room as needed. Corridors are considered rooms (just thin), where they bend they are in fact two overlapping rooms and should be merged during the same process.
     
    After rooms have been merged, the geometry builder works on the new set of room tiles to spit out all the required meshes, lights and props.
     
    Infinite Power!
     
    In theory we can make an infinite dungeon if we move the rooms around us at the origin and use world space as a random seed for new rooms. An order of magnitude more advanced than Very Big Cave Adventure.

    10 PRINT "You are in a cave (N,S,E,W)?" 20 INPUT a$ 30 GOTO 10
     
    Out of time!
     
    Yes I've run out of time for part 02 due to code re-writes. I'll write up the room generator code as part 03 and post the example code with it. Then we'll start to see the two halves coming together and creating our procedural maps.
     
    Preview: HUD Overhead Map

    First pass on HUD to show location of rooms (with ID) within the randomly generated map complex.
  3. Flexman
    About: This is a short series for the Leadwerks community on the process of creating a simple game using procedural content.
     
    A short while ago I was approached to write a book on terrain generation using dedicated tools for a variety of game engines. There's a lot more to game content than just terrains. Josh's recent blogs on CSG got me thinking about one of my favourite games and had me itching to re-visit the genre.
     
     
     
    This series is an exploration of topics I don't normally exercise but are fun to do. Leadwerks is a fun engine that's ideal for prototyping ideas. So throw out your terrain system, we're going to make a game all about claustrophobia. We're going DOWN into the tunnels to blow stuff up Leadwerks style using a mix of old-school gaming know how and modern day 3D.
     
    Our goal: create a simple six degrees of freedom shooter set in a procedurally generated network of rooms and corridors. You can use this as the basis for a number of different games but as I'm a fan of the old classic shooter "Descent" and it's easy to create a physics controller to fly around in Leadwerks. Also it can be re-worked using Leadwerks3Ds CSG functions when it's available.
     
     
    Procedural Map Generation
     
    Dungeoneering is back in gaming news with Legend of Grimrock, a take on Dungeon Master grid based geometry. Many games are based on grids, they are easy to work with and often form the basis of AI routines. They are also easy to generate geometry for. There have already been numerous articles and blogs on creating such levels, you'll find links at the bottom of this article.
     

    True Story: The Survival horror Amiga game Xenomorph copied the same gridded dungeon mechanics in a blatant rip-off of the Alien universe where stone walls were replaced by ship corridors. Coded by an ex-Microprose employee I visited his house somewhere in Devon many years ago. He revealed that many of the game objects I clung onto for dear life had no function whatsoever. Lesson learned: Don't explain everything, keep the mystery, it's dishonest but more fun for the player.  
    But before we look at some LUA code to create geometry we'll first have a taster of different techniques used in creating random level geometry. After that we'll kick things up by adding more vertical elements for our demo game. I warn you in advance, I'm not a LUA expert and nor am I a big fan but I'm sure you good folks can make it better and LUA brings the examples to the widest possible audience.
     
     
     
    Node based grid
     
    Our goal is to create a collection of rooms connected by corridors through which we can explore. A simple approach is to seed a random number of nodes (rooms) and connect them up. There are other methods, such as a corridor based approach which adds rooms in a second pass, such a method is used in the classic DOS game HACK (aka NETHACK).
     
    For our examples we'll stick to a 40 x 40 grid to keep it simple. Each grid square could equate 100 world units or 10 in Leadwerks, it doesn't matter as long as we use a consistent scale. We'll stick to 100 units (1 unit = 1 meter) per grid square for this example meaning our potential map is 400x400 units.
     

     
    Here each node represents a room of random size. We can restrict the size of the room to fit within its parent grid to make it easier to deal with problem of overlapping rooms as illustrated in the figure below. Eventually we'll construct the geometry to build these irregular shaped rooms so they merge as a single entity.
     
    You can see that choosing the size of rooms will impact on how much corridor space you're going to end up with per map. This is something you can set in code easily giving control over how open or closed your level will feel.
     

     
    If we stick to the grid arrangement connecting them by corridors becomes an easy process. The difficult part is constructing the vertices and triangles to fit together nicely but we'll get to that. By far the easiest way of joining rooms together is by joining them at the cardinal points of the room. While this makes logic easy it also makes for a pretty boring layout. What's more, there's no reason why a corridor has to be of fixed width...or height.
     
    A room typically connects to a room in a neighbouring grid using the straightest link possible and we'll dog leg corridors as required. To add some interest we'll create a 'route' through the grid and randomly connect to neighbours.
     
    Again there are many ways you can do this and I encourage you to try your own variations. Our rule-set will have a random chance of connecting to a neighbour if that room. That chance will degrade if that room overlaps and on the number of existing corridor links. Hopefully our final 2D layout will approximate the figure below.
     

     
    Then each room can add its own flavour by adding columns, point lights, props, whatever fits your game.
     
    Once we've got a class to generate the map we can then move on to generating the required geometry (surfaces) so we end up with something looking like...

     
    Things get even more exciting when we'll extend these maps into 3D, with vertically offset rooms and corridors. Eventually we'll have a complete base generated by a single random seed you can fly, shoot, leap, run through with a basic controller.
     
    Once we make the logic for generating rooms 'furniture' props can be parented to the room mesh to take advantage of culling. I'm not going to add much in the way of occlusion, instead Leadwerks can take the stain and should do a good job with no effort on our part.
     
    In part 2 we'll start with some code to generate the above with geometry directly in the Leadwerks editor and we'll be using Vertex and Surface commands to create rooms which look like the screen-shot below.

     
    Random room generation in LUA, corridors coming shortly for part 2

     
     
    Everyone is welcome to contribute.
     
     
    External Links
     

    Procedural Content Wiki http://pcg.wikidot.c...geon-generation
    Generating Random Dungeons http://dirkkok.wordp...article-series/
  4. Flexman
    Just a short post since I'm busy being pulled in a dozen different directions; I got roped into helping someone create a short health and safety film, drafting a project proposal, raising finances and even managed to fix a long standing bug in Combat-Helo. Now the sensors, line of sight and all that guff is now WORKING again, Hooray. A quick background on that, an engine update needed to fix some occlusion issues slightly changed the Model hierarchy in a subtle way that changed how actual filenames of models were retrieved, long story short, they no longer matched the ones in the vehicle database as they had "lod1" suffixed to everything . But hey it was a quick fix once traced, I digress as usual.
     
    Talented Paolo, creator of many a fine mascot has been working on some fictional nose-art and patches for the squad mascot in Combat-Helo. We knew we wanted it to feature a mosquito, the mosquito is the nickname allegedly given to the AH-64D by Taliban forces. Incidentally it was also the aircraft my father was attached to during World War II so I felt it had a double meaning.
     
    The character will be visible on squad patches and eventually be available as nose-art.
     
    "Mossie" copyright 2012 Paolo Pomes
    Below is an earlier draft of "Mossie" to help decide on nose style, the nose later became the Apache's signature Bushmaster cannon.
     
     



    Please post your thoughts and comments on "mossie", suggestions for what he can hold in his left hand are invited.

     
    Source
  5. Flexman
    I know it's a little late for Valentines Day. Excessive flapping, bumping and separation is discussed in this priceless US Army video from 1980. If only they had "RotorCam ™". The Apache has a fixed rotor, mast bumping isn't an issue but it's all part of helicopter theory.
     

     
     
    I've been forced to continue hunting for other employment to deal with a mounting debt crisis. As a result work was put on hold the past two weeks (hence the lack of updates). I apologise for letting my post slide, the project really is taking a toll. I've been ordered to get back to Combat-Helo ASAP (taxes and all that business stuff needed to be done too), I'll be updating as well as I can. There's an engine update to finish rolling into the project (I started on that earlier in the week, this fixes a problem with terrain and I hope the EntityUserData and hierarchy problem).
     
    A Twitter follower suggested I post some Gun Cam footage, I had a look at that and found vegetation range/LOD issues in the MTADS camera need dynamic adjusting (not sure what the most efficient method will be yet).
     
    I'll just round this entry off by stating that raising funds in the UK for technical entertainment projects is very difficult if not near impossible. Bank staff/accountants take one look at video of Apache cockpits and give you funny looks like you're some kind of gun nut (that actually happened).
     
    In the meantime, enjoy the technical detail of the above video.
  6. Flexman
    Swimming was one.
     
    Setting up controllers in XPlane is another.
     
    Yes the things you thought were going to take a few days which turned into a week and a bit (you could almost say 'two weeks'). Josh at Leadwerks gave me a leg-up on how to put an MMO style launcher together and I've almost done wrapping up all the security for the necessary database, key code generation, CVS style updates and all the stuff we need to match individual content to clients. We'll make available downloadable content (DLC) such as new maps and the occasional helicopter (the planned CH-47D), not to mention the odd mission pack.
     
    No, this key won't work, it's a fakeSo there was a lot to do to make a simple one shot MMO style launcher. The back-end server stuff had to be built from scratch. The launch .EXE calls home, applies on-demand updates and preps game settings for launch. For simple security sakes I layered server-side scripts for handling the updates, admin and audit. Auditing is handled by triggers, SQL injection is futile since only one procedure is exposed to clients.
     
    Any DBA will tell you Auditing is an important part of any security. If you build any kind of database exposed to the internet and you don't build your own layer on top of any default transaction logging then you're kind of asking for trouble. The database engine used here is not known for keeping detailed logs so it was necessary to build one anyway. Things you might want to think about if you ever need to create systems like these are making log indexes suitable for frequency analysis (this one method Twitter uses to spot spamming), fake open doors and keys and silent alarms. My favourite trick in the past was a fake database that gives the intruder a high and makes them believe they have walked away with the crown jewels while evidence is collected. That sort of none-sense is not required here, I kept this one functional with a tiny footprint.
     
    The Pilot ID we'll be sending out is the key to your content; install once, play as you like. If you share copies around on your network you can launch 'offline' no worries, no need to be connected to the internet until you want to update. It's nice if you want to buy multiple copies for your network and support the effort put into this project but I'm poor too so I know how it is.
     
    Little bit of script consolidation and testing to do before we let this loose in the real world.

     
    Source
  7. Flexman
    Particle engines are commonly used for fire and smoke effects adding a lot of eye-candy for little effort. Every frame an emitter creates a number of billboard sprites along a vector. Normally this is not an issue when movement along this vector between frames is quite small.
     
    But what if you're talking about something that moves really fast between frames? Such as a rocket launcher or space-ship? Fellow Brit and indy developer Cliff Harris of Gratuitous Space Battles fame ran into the same problem. Here's a screenshot from his blog of a rocket.
     

     
    The rocket moves too far between frames to space out the particles in a pleasing manner. By adding a line of particles between each frame update a more pleasing effect is achieved.
     

     
     
    In Combat-Helo the hero-ship is typically loaded with several 70mm rockets that accelerate to 700 meters per second in approx 1.5 seconds. Even maintaining a short smoke trail of 10 meters can't be maintained as the rocket distance between two frames might be 50 meters or more.
     
    A linked list of billboards (TMesh CreatePlane()) is being trialled. UsingMacklebees billboard shader tweak and changing...

    ... gl_Vertex.x,gl_Vertex.y to ... gl_Vertex.x,gl_Vertex.z
    ...to work for Planes a 2D billboard function was implemented which handled colour and timing properties with a deviation (waver) offsets.
     
    This creates a ribbon of 50-150 quads (TPlanes) aligned to the camera using the billboard shader. Scale of each quad is roughly double the space between them to ensure some overlap and consistency. The smoke texture has baked lighting with a normal map however a normal map is redundant since the quad is camera aligned and rendered in the transparency layer.
     
    The spacing produces a good fill between two positions each frame. In the example image below the length of the trail is 75 meters, each particle doesn't require much updating since this was written for a specific purpose. As the shader takes care of orientation only timer tests, waver and alpha-colour needs updating although some extra instruction for management could be implemented (see footnote).
     
    Don't attempt to use ScaleMesh() as that slows the whole pipeline down. If you need to change the size of a particle you'll either have to do it in the shader or delete the particle and replace it with a larger one.
     

     
    Same again showing the AABB of each TMesh/Plane.
     

     
    That's about as fast I can can come up with using LE commands. The next stage is to replace the TPlane billboards with a single entity built using a TMesh and adding triangles as needed.
     
    Other optimisations might include changing the number of particles depending on the 2D length between the head and tail. We need more 'filler' if seen from the side than head on.
     
    Worst case would be a full salvo from 4 pods (each pod carries 9 rockets), in this event the particle manager could limit them. With the Leadwerks emitter function, once created you can't change the number of particles so having this level of control is handy for all manner of effects that need adjustment over time. This is of course slower than the LE particle system.
  8. Flexman
    Fingers crossed the weather is not too bad tomorrow at Gilze-Rijen air force base. Our man in the Netherlands is prepared for a day of audio recording for the Apache and Chinook.
     
    Good luck Reck. Weather is pretty lousy here, high winds, low temps and rain, sat images and forecast is so-so. Expect wet weather and don't forget the 'dead cat'
     
    Source
  9. Flexman
    Spent a lot of time today house cleaning. No not code, literally the house. It's amazing what teenagers can do to your treasured Monty Python DVD collection, it's not funny. Stop that, it's silly.
     
    I over-complicated the map drawing and stripped it down. Added functionality to the TSD page for controlling the map scale. I'm happy that it will work with 3rd party maps too. Oh I just noticed in the screen shots the scale index is off by one.
     

    The green colour is from the low elevation range but I blend some of the terrain colour too which I should perhaps leave out since it gives a false impression. I'll add a button on the page to cycle map modes rather than have to go through a sub page perhaps. Otherwise it might get (more?) confusing. Hey, Longbow 2 had one button to cycle through NAV/ATK and BOTH....and no map unless you flew the Kiowa (I think).
     

    So tonight I'll add the aircraft datum (the helo symbol on the map focal or point of rotation) and begin adding the glyphs for the waypoints and other symbols that we want to appear on the map. Should finish adding the waypoint data while I'm at it (ETA etc). I can't work out endurance time yet as the fuel data isn't yet present. It varies with payload and altitude, there's a table in the A model dash-10.
     
    Would like to have an elegant algorithm to approximate it.
     
    Source
  10. Flexman
    Another little progress update but first a diversion. Yesterday I took some time-off to brush up on gaming skills and really enjoyed Disney Interactive's "Split Second", one of the few car games I keep going back to since I typically find racing games tedious (lack of skill and patience on my part). However this game is almost pure adrenaline and I find my self coming back to try and shave off 2 seconds while avoiding exploding aircraft and collapsing control towers. Split Second is actually the perfect arcade car game IMO and probably much overlooked in a busy genre. It screams for a post race replay option though.
     
    Enough digression. Not much to show yet as I've only spent an hour or two on it this morning, I hadn't put in the VR cockpit data for the TSD buttons so went and put that data in. Had some color issues down to a mix of LE and OpenGL and sorted out the MPD scale for rendering the currently loaded nav map image.
     
    TSD stands for Tactical Situation Display
     
    It is the most complicated system in the helicopter. Which I will strip down to the bone for the first release otherwise I'll be at it for months. The TSD presents a 'Gods eye' view of your helicopter and your environment. Downloaded target information, friendly unit sightings, route navigation and more is presented here. It is a moving map system that can be frozen, panned and scaled.
     
    I need to slim it down into something manageable. Also there are many functions that are desirable to use in the command HQ tent planner and also other helos in future.
     
    Our TSD will feature
     
     

    Fire zones (aka PFZs, old school but everyone will want them) Route editing Threat, target and hazard objects Nav and Attack mode (PHASE) Freeze, panning, zoom Crewman cursor acquisition, click on map and point will be made virtual in the helmet display.
     

    So there's a few things to do but also shared with the ground planning system on the HQ tent computer.
     
    For the base map, it's using the TTerrain() object on the loaded terrain entity (I needed to cast it to get access to the height and normal map data otherwise it's considered a TEntity). A shader converts the normal map into an intensity multiplier ( float intensity = dot( normal , lightdir ) ) for the colourmap, multiply the diffuse and intensity and voila you get a shaded relief map just like the one in the Leadwerks editor (except I don't need the realtime light position and colour that the editor uses).
     
    To this I'll add a colour lookup table from the heightmap and possible some contour lines. I've not used an "lut" function (look up table) in a shader yet so it will be a fun little exercise. Contour lines could be done by setting output colour if the heightmap value is a multiple of 10 (meters) or whatever value I pass to the shader. This just colours the pixel if the value is in that multiple, steep terrain might bypass that value so results will be a bit patchy depending on the source data. I'll look into a proper contour function.
     
    Keep in mind the heightmap in terrain doesn't have a flipped y axis.
     
    Source
  11. Flexman
    The HMD of the new MTADS Apache comes with a number of 'virtual' items displayed to the pilot. The currently selected waypoint is made virtual in cruise and tranistion modes, the waypoint position is marked with a flat bottomed diamond with a dot in the centre. See the take-off point (W00) on the image below.
     
    Flying with the flight path marker inside this symbol and you should head right to that spot.
     
    Waypoint marker
     

     
    Source
  12. Flexman
    Another disaster strikes, PC wont boot at all. Strange thing happens on the BIOS screen, the moment any attempt to detect any drives (and I tried a few different drives that I know were fine), a bright underscore character appears under the circled comma (picture above).
     
    So apart from my laptop I'm now without a working PC and no means to fix it atm :/
     
    Source
  13. Flexman
    More flight symbology added today. Raining almost non-stop and my cars wiper motor has failed, so wasn't going anywhere.
     

    We added a couple of items that make it possible to do some precise flying.
     

    Velocity Vector Acceleration Cue The Velocity Vector is the blob on the stick that originates from the cross in the centre of the HMD. This is a representation of the direction and speed of your aircraft. Think of these as "top down" views with your aircraft in the middle, forwards movement is up, sideslip left and right. It's scaled so that the top of the HMD (below the heading tape) represents 60 knots. This scale changes depending on the mode but for now I'll stick to the TRANS (transition) which is the most used.
     
     
    Floating around the end of the "stick" is a circle, that's the Acceleration Cue which shows your helicopters acceleration. Generally the blob on the stick will steer towards the Acceleration Cue as you manoeuvre the aircraft.
     
    Using these cues it's possible to perform some precision flying and arrest unintended movement before it becomes a problem. Learning how to read these will improve slow speed manoeuvres approaches and landings.
     
    I also changed the rate of climb indicator to a solid pointer, slip ball is currently being added. And basic waypoints and steering cues will probably be done this weekend. Especially if the weather is still lousy.
     
    Source
  14. Flexman
    HMD = Helmet Mounted Display
     
    Remember when Virtual Reality was going to be the next big thing? Seems odd having to re-create something virtual in a game that is virtual, the modern Apache has evolved a bit since the late 1990s. Being able to display in the pilots 3D monocle (yes that's a joke) a lot of data to the crew about targets hidden behind objects, terrain contours. These 'virtual' items are matched 1:1 with the crews head movement unlike a lot of the symbology that is static.
     
    The first one we're going to look at is the "head-tracker". This is a broken diamond that represent the nose of your aircraft. As you look left you should see the diamond maintain a relative position to the nose of the helicopter. For give my "Novalogic" terrain map.
     


    I had a bugger of a time getting the function to match up to the outside world until I worked out I was missing the aspect ratio of the window (the HUD has a scale factor too in case it's too big or small for your resolution). We're ready to add the waypoint indicators and LOAL/LOBL missile constraint boxes. The flight path indicator is another one, that represents the position of the aircraft in (I think) one second? Have to check my notes on that.
     
    You might see some alerts on the UFD that shouldn't be there, as always it's not final and I know about them.
     
    The cockpit seems slightly offset from the absolute outside world position, now the crew does seem to sit off axis but I'm checking this out. The camera positions are absolute and bang on the X axis zero line of the aircraft and not directly centred on the crew seats. It looks a bit odd otherwise. Looking into that anyway.
     
    Source
  15. Flexman
    Been pretty ill this last week, progress has been sporadic. I took time out to rebuild the engine code so it's now the same for all Apaches, AI and player alike.
     
    Pilot feedback from screen-shots and videos also allowed me to rebuild the whole engine start-up and ENG display page. Automatically switches to in-flight mode. Things blink, change colour, are boxed appropriately. Couple of bits left to do, the biggest headache was dealing with all the fiddly scale changes and adding new code to simulate power transfer which needs some attention. Here's the page with some values thrown in to show some extremes. The whole thing is worthy of a tutorial video sometime.
     

    Still have some parts to add to the engine simulation, oil, temp. Rotor speed and torque needs values from the new flight model to work properly but essentially it's just polling values from the helicopters state. Oh another thing I didn't put back in, the actual transient timers, these are counters that show how long you're flying outside of normal parameters which can shorten component life or lead to failure.
     
    The new alert system which I also had to overhaul is ready to drop the betty samples in en-mass.
     
    Will tidy this up tomorrow, look at where we are with the new flight model and see about the Leadwerks 2.40 engine upgrade. The PHY situation is not bad as I thought since we're not using LE for vehicle physics.
     
    Source
  16. Flexman
    Actually the rotor brake system. I'm not really clear what would happen if the rotor brake is off when you move the power levers from the Idle to Fly position. Procedures say DONT, return the switch back to on until the engines are at flight speed (approx 100%). Once the engines are happily settled at flight speed flip the brake off and the rotors begin to turn. Watch the rotor RPM gauge on the MFD until it's in the yellow close to 100% then you're good to go.
     
    Conversely, I'm not too clear what damage might occur when then rotors are at 100% and the rotor brake switch is is turned on. A lot of heat and grinding metal noise? A nasty letter from the Boeing company? Currently I just have it decay the RPM to zero under a constant until I get a better answer.
     

     
    I added more realism options to the config.xml for control over individual items like startup, targetting, flight model.
     
    OOOH, I just remembered why I was really happy today. AD fixed the road physics problem by using displacement mapping in 3DS MAX. So no hacky hacks may be required for road driving afterall. Well done sir. I knew I was feeling buoyant for a reason. If you want to know what the hacky hack means, the road surface was aligned to the terrain with a vertex shader which moves each point on the model level with the ground (relatively) but physics engines don't know what vertex shaders do to geometry, as far as they are concerned the model is unchanged. So there's a disconnect between what the physics engine updates and what the 3D card renders on screen. Using displacement mapping with our heightmap as source, the road meshes are baked at the right elevations to fit, the physics model and visual model are now united and traffic may now use the roads without any problems.
     
    So, way to go AD. Another cracking solution.
     
    Source
  17. Flexman
    I'm putting together specifications on the mission side of the game. To me, the bit that you 'play' is every bit as important as the simulation side, and while it's taken a back seat for now, things are getting to the point where it's time to get on and make it all work. Going through some of my older material on this, it needs to play like Longbow 2, have the crew management elements from Gunship 2000 and strategy elements from tactical board games on guerilla warfare and a bit of a story element that wouldn't be out of place in a Chris Roberts game.
    So where do we start? Units, missions, events and triggers.
    Units perform missions, missions contain waypoints, events and triggers. So lets start at the top and work our way down.
    Missioneering
    Not many will remember the last iteration of my mission planning tool and generator, Missioneer Delta pictured below, circa 2000, seems like almost 10 years ago :/

    The plan is to put in something similar onto the command tent terminal you can operate to change waypoints and assign untasked airCREW to helos to carry out listed missions. In some ways this is not unlike Enemy Engaged. Missions are not necessarily assigned to aircraft (they can be), but we'll assign them to the player, AI pilot or crew. Like the arming system, it will be possible for a player to take the role of a commander and assign missions to aircraft that pilots execute.
    You can walk around and choose an aircraft that's not already assigned (see it's info plate when on foot for status). In future we hope to have a selection of helicopters/vehicles (CH-47) but we're focused on the Apache.
    When the player boards the aircraft, this is like loading a data cartridge with your mission detailed into the aircraft's avionics. The TSD will be updated with waypoints, times and mission page populated accordingly. First man in sets the mission.
    The mission ID# key is set in the vehicle entity. This is reset on one of two conditions, the player has exited the aircraft for 5 minutes or the master zeroise guarded switch in the cockpit is operated. In the case of the latter, a new mission profile can not be loaded until all crewmembers have exited the aircraft.
    Mission ID# is just a key reference to the hosts list of active missions which can be viewed on the command tent terminal (connected to the projector).
    But we're getting ahead of ourselves. Missions and units need a detailed specification which is the point of this blog entry and my current task.
    Running under the surface of the simulation will be the all important campaign engine which iterates through units. We'll take the term UNIT to mean a collection of vehicles, a "group entity". Lets look at an old user editable unit form, take from Missioneer. Nothing really remarkable about it, just happens to be handy.

     
    So we can see some of the basic parameters of a unit. There's a list of entities than make up the unit, a 'formation' which is a list of relative positions that each member of the group will attempt to follow (we'll have some behavioural exceptions to this). An orientation, direction to face when at rest. A name, something fancy and military. Skill level, agression and some factional information.
    There are also some timers, our game runs in a real-time 24 hour clock cycle (with time advancement). So unit timers might be relative to an absolute time (world time) or mission time (from dust-off). Timers have an enabled/active status too in case they need to be activated by triggers.
    For example, if we want a group of insurgents for player assigned patrol mission to spawn when a player has taken off, the mission engine will assign a MT (mission time) of "00:00:00mt Active", but we might want them 5 minutes into the mission, or not spawn at all except by some trigger, such as a player entering the engagement area.
    Or we can set the unit destruction time (just means the de-spawn time). For example, a rescue against the clock, get to them before they are discovered by encroaching insurgent forces. Once depawned, that would trigger a unit removal message which another event trigger can listen for, signalling a mission fail.
    So timers are simple unit level events that require no special handling by an event system. But for our game to handle recognition of a "win" or a "fail" situation we'll need to add a basic "cause and effect", a "cause" being destruction of unit "x" with the "effect" being "mission complete".
     
    Mission Events
    SpawnUnit (unitID) DestroyUnit (unitID) SetUnitWaypoint (unitID, waypointID) SetUnitFlag (unitID, active|weapons_free|godlike|formation|state, param) SetUnitFaction SetMissionResult SetWorldFaction( factionID, delta) SpawnObject (objectID, parameters) CreateWaypoint (unitID, waypointID, location, speed, altitude) Play Audioscript (script or audio filename)
    Mission Triggers (in any active combination)
    MissionRating (percent, win|loose|draw) MissionTime (+ - secs, MT|LT) UnitDistanceFrom (unitID, targetUnitID, distance_meters) UnitDamage (unitID, damage_percent) UnitWaypoint (unitID,at|before|after, waypointID) UnitStatus (not_in_combat|combat|flee|panic|landing|takeoff) UnitInZone (unitID, zoneID) FactionDelta (factionID1, factionID2, + -value) DiceRoll (percent)
    These are enough for the campaign and mission system to generate pretty much most situations.
    Now the tricky part is to build the logic to generate seemingly intelligent behavior and entertain missions using this small command set. And here is where we can have a lot of fun.
    We could extend the complexity of the logic further by adding a LUA Get/Set interface for the event system. Then user made missions can apply some complex logic. Even creating whole missions from a LUA script. This is not something we want to do at this time. But for the future, it could be done.
     

     
    Source
  18. Flexman
    Finally a video showing the free flight mode we really wanted to show at Summer Sim 2010. Thanks AD for doing an amazing job of making it all look good.
     
     

    http://www.youtube.com/watch?v=Y6ILqIQDaio
     
     
    Sorry about the lack of blade-cam, LUA object reference problem. Will get it sorted.
  19. Flexman
    AD has been working on a new FOB which will be the "other" main camp you'll be deployed at during the course of the campaign.
     

    It's a triangle arrangement used by some forward operational bases. Here the CH47s are ready to be deployed.
     

     

    And finally..
     

    Don't have a name for the camp yet.
     
    Source
  20. Flexman
    Dave, the kids must have been playing "pilot" again, they've gone and pulled off the knob for the standby instrument lighting, make it like the other two rotaries. I'll fix the camera clipping on the seat
     

     
    Source
  21. Flexman
    Had a quick play with Saitek's DirectOutput SDK, results are mixed but not bad. The MFD resolution in Combat-Helo is a native 512x512 and the Saitek Flight Instrument Panel is VGA 320x240, there's a bit of difference.
     

    Sampling the buffer down to a smaller surface and then adding a suitable BMP header to send over to the device. Remarkably easy. I'll have three more please Saitek
     
    BUT please can you make an FIP version two with a 512x512 square ratio display? Or at least increase the vertical resolution to 256?
     

    Text is near impossible to read, but the general layout is recognisable. I don't know if we're going to support these devices in game, it's just one more thing to go wrong and support, and the default button locations don't match up so it would need code to move functions over to the left side, there are only 6 six buttons. But it's interesting to play around with.
     

     
    Source
×
×
  • Create New...