Jump to content

reepblue

Developers
  • Posts

    2,486
  • Joined

  • Last visited

Blog Entries posted by reepblue

  1. reepblue
    I've started on my quest to make a full C++ has started today. I've noticed that LEX2 via the project manager didn't copy over as clean as it should, so I had to make the project from scratch which wasn't a big deal (for me). I should look into a way of getting that fixed.
     
    After setting the project up, I noticed a few errors I was getting after I commented out the LUA_GAME preprocessor. It was a quick fix, and development went under way after that.
     
    For this project, I'll need things that's already available in Lua as part of the SDK. The high priorities are the first person player, animation manager, first person weapon (the pistol) and the Crawler's AI. As I said before, I don't want to convert SDK scripts to C++, but instead write the code myself. I'll look at the Lua stuff only if I get stuck.
     
    Today I found out that not everything will work when you convert Lua code to C++. I decided on the player as I've written it for C++ with my actor class, then converted it to Lua because of problems. Now I want to but it back to C++ as a puppeteer. It took a few hours line by line converted the code. When it was all done, I ran into a few issues.
     
    First off was HOW to spawn it? I could use a puppeteer script like I mentioned in my last blog, but the player should be spawned dead last. So the best method was to create the player in LEX2's OnPostMapLoad function.
     

    // This function is called when the program finished loading a map.
    void OnPostMapLoad()
    {
    Entity* puppet = WORLD->FindEntity("Player");
    FPSPlayer* player = new FPSPlayer(puppet);
    }

     
    This function is called at the every end of the level loading process. This is mostly so we don't hear the weapon being picked up while the map is loading, or a spawn sound like the Vectronic Demo.
     
    I was then running into null values that were driving me crazy. I went back and predefined all empty values as NULL and I was still getting a crash. It turns out, I didn't have the bool function returning the right value.
     
    Next was the footstep sounds. As I said before, I copied my fully working Lua player script and I noticed that the footstep sounds were playing too fast. I defined the value lastfootsteptime as an int in the header as 0, but when I did a system print of that value, it'd always return as 1. the fix was to define the variable before the footstep function instead of in the header as so:
     

    int lastfootsteptime = 0;
    void FPSPlayer::UpdateFootsteps()
    {
    if (lastfootsteptime == NULL) { lastfootsteptime = 0; }
     
    // If we're moving and not in the air, play a step sound.
    if (input[0] != 0 || input[1] != 0)
    {
    float speed = entity->GetVelocity().xz().Length();
    if (entity->GetAirborne() == false)
    {
    if (speed > MoveSpeed*0.5)
    {
    long t = Time::GetCurrent();
    long repeatdelay = 500;
    if (t - lastfootsteptime > repeatdelay)
    {
    lastfootsteptime = t;
    PlayFootstepsSound();
    }
    }
    }
    }
    }

     
    Last, I had to fix a Gimbal lock with picked up objects. This was because all variables in Lua and C++ work differently. So when going the lines one by one, I stored rotation values in a Vec3, and not a Quat.
     
    After the first day of my summer game, I got a player that walks around, jumps, can crouch under tight spaces with working sounds! I just got a few things to tweak before going to the next thing which will probably be the animation manager.
  2. reepblue
    Note: This is pretty much a copy and paste from this post here with a few tweaks.



    After 3 years of planning, modeling, texturing, and a lot of engine hopping, I was able to create a playable demo in the Leadwerks Engine for Windows and Linux (Debian/Ubuntu/Mint). You can download the demo from this page. A small demo was created not only to test the basic elements of the game in the new engine, but to also give back to those who have been supporting this project since I started talking about it.
     




     



     
    I'm also announcing that from here on out that the development will be more open. How open? Well first you can always check the Trello page to see the progress at any time, If you wish to help, drop me an e-mail. Feed back is welcomed with opened arms. You can do this simply by e-mail or twitter @reepblue. I'll do my best to respond if necessary.
    About Vectronic
    Vectronic is a first-person environmental puzzle game inspired other games of the same genre such as Portal, Quadrum Conundrum, and Q.U.B.E. Players must solve puzzles using special power balls that effect boxes that changes their behavior in the world. For more information, please visit the Q&A. For additional help, click here.
    Please let me know what you think. Although a short primitive demo, this is a big milestone for the project in the indie path.
  3. reepblue
    With the recent popular substance shader by Shadmar, more talk and focus has been about cubemap reflections surfaced in the community. Reflections in Leadwerks were done in the past with the SSLR post process shader which made all surfaces reflect the world in real time.
     
     
     
     
    However, it had the following problems:
    It was applied to ALL materials in the scene. If you had a material that wouldn't normally reflect such as concrete or wood, the shader would still effect those materials. Basically, the texture artist had little control.
    The post process shader needed to be first on the list, and some people didn't understand that.
    You could adjust the reflections by the model's alpha color. But if you had any shader that relied on the alpha mask for map blending or something else, you're out of luck!
    The reflections distorted based on camera angle, and not really great for in-game action.
    Like everything OpenGL, there is/was a chance it wouldn't work well with AMD gpus.

     
    This was all we had until in February, Mattline1 posted a blog on his PBR system, which included a block of code that built a world cubemap and swapped the cubemap texture with it. Thankfully, he uploaded is source code so anyone who has the professional edition could download his code and have a look.
     
    While his implementation is very nice, it has the following design flaws for a typical Leadwerks user.
    You needed to use only the PBR shader. Normal Leadwerks shaders didn't render well.
    Needed a Post process effect to adjust the gamma of the scene.

     
    Weeks later, Shadmar uploaded his version of a Substance/PBR shader which allowed users to still use their existing shaders, and gave artists more to work. Since it's a shader, and not a full implementation like Matt's, it didn't have the world reflection system. I've been busy with a lot of Source engine work, but seeing how much interest this is getting lately, I decided to take a few days off and see if I can make my own cubemap system.
     
     
    The Implementation
     
    A disclaimer, this is not a full "How-To" as you'll need to add it to how your application, and it'll most likely do things differently than how I have my application set up.
     
    First, we need to figure out how we are gonna get a world cubemap to a texture. While it's not the most "next-gen" way of doing this, I decided to base my system off of the Source engine because, well I know how it works, and it's pretty simple. It goes like this:
    Mapper places a env_cubemap entity near a shinny surface. (Any material set to use cubemaps.)
     
    Mapper compiles and runs the engine.
     
    Mapper than types "buildcubemaps" into the console.
     
    For each env_cubemap entity, 6 "pictures" are taken (up, down, left, right, forward, and back). than those textures are saved within the bsp.
     
    The map restarts, and the surface will use the near by cubemap texture.

     
    Great, that seems very straight forward, and we can remove steps 2 and 3 as there is no compling in Leadwerks, and everything should just work. The first thing we need is a 3D point in the world that'll act as our env_cubemap.
     

    class Cubemap : public Pivot
    {
    public:
    Cubemap(Vec3 pos = NULL); //lua
    virtual ~Cubemap() {}
    ...
    ...
    ...
    };

     
    We build off of the pivot class because we want this to actually exist in our world, not be a "puppeteer" for a pivot entity. We also have the position being set in the constructor as the only way to get our custom entity into the world is by a lua script. Using ToLua++ we can make a script that we can attach to a pivot/sprite.
     

    function Script:Start() local n = Cubemap:new(self.entity:GetPosition(true)) self.entity:Hide() end
     
    This script will spawn our cubemap entity at our editor placed pivot, then hide the editor placed pivot. Basically, we swap the entities during level load.
     
    This cubemap entity job is to search for near by entites, (Which I currently have set to it's aabb radius + a range float) then change it's surface material with a copy of what it had, only with a different cubemap texture. Early in development, I just have the entity fix the cubemap texture to the current world's skybox to make sure that this would work.
     

     
     
    void Cubemap::SetReflectionTextureToSkybox(Leadwerks::Surface* surf)
    {
    if (WORLD == NULL) { return; }
    if (WORLD->skyboxpath == "") { return; }
    if (!surf) { return; }
     
    Material* mat = surf->GetMaterial();
    if (!mat) { return; }
     
    // Make this faster, if there is any indication of a surface not using the substance shader, abort the cubemap application!
     
    // If there is no cubemap assigned, then we can skip this object.
    if (mat->GetTexture(6) == nullptr) return;
     
    // if the metal value is 0, we can skip.
    int matspecmetalness = mat->GetColor(COLOR_SPECULAR).w;
    if (matspecmetalness == 0 ) return;
     
    Material* copy = (Material*)mat->Copy();
     
    if (copy != NULL)
    {
    Texture* cubeTexture = Texture::Load(WORLD->skyboxpath);
    copy->SetTexture(cubeTexture, 6);
    surf->SetMaterial(copy);
     
    cubeTexture->Release();
    copy->Release();
    }
    }

     
    Before we go into building the world cubemaps, we need to add this to the entity's header. This will make it so we can pass a texture to the entity:
     

    Texture* SavedMap;
    Texture* GetCubemap()
    {
    return SavedMap;
    }

     
    Now to the cubemap factory, which on map load will find all of our cubemap entites, do the texture building or each entity, Set SavedMap to the new cubemap texture, then tell the cubemap entity to look for near by entities so it can do it's replacement job. Thankfully, all the yucky stuff has been done by Matt, so I just needed to cut the stuff I didn't need, and make it handle multiple level loads and disconnects to work with my worldmanager class.
     
    Here's a few snip of this process.
     

     
    bool CubemapFactory::initialize()
    {
    Release();
    ...
    ...
    ...
     
    int i = 0;
    do
    {
    if (WORLD->GetEntity(i)->GetKeyValue("cubemap", "0") == "1")
    {
    Cubemap* pCubemap = dynamic_cast<Cubemap*>(WORLD->GetEntity(i));
    if (pCubemap != NULL)
    {
    // Build the cubemap
    cubeTexture = Leadwerks::Texture::CubeMap(
    reflectionResolution,
    reflectionResolution,
    Leadwerks::Texture::RGBA,
    Leadwerks::Texture::Mipmaps
    );
     
    cubeTexture->BuildMipmaps();
    Leadwerks::OpenGL2Texture* gl2cubeTexture = dynamic_cast<Leadwerks::OpenGL2Texture*>(cubeTexture);
    glActiveTexture(gl2cubeTexture->GetGLTarget());
    glBindTexture(GL_TEXTURE_CUBE_MAP, gl2cubeTexture->gltexturehandle);
    glEnable(GL_TEXTURE_CUBE_MAP_SEAMLESS);
    glGenerateMipmap(GL_TEXTURE_CUBE_MAP);
     
     
    // Apply it!
    GenerateReflection(pCubemap->GetPosition()); //<-- generate first reflection texture
    GenerateReflection(pCubemap->GetPosition()); //<-- use initial texture to generate correct texture
    if (cubeTexture != nullptr)
    {
    pCubemap->SavedMap = cubeTexture;
    }
     
    // Tell the cubemap to look for near by entities, and replace it's cubemap texture with cubeTexture
    pCubemap->SearchForEntites();
    cubeTexture->Release();
    }
    }
    i++;
    } while (i < WORLD->CountEntities());
    ...
    ...
    ...
    CubemapFactory::init = true;
    }

     
    GenerateReflection() is pretty much the same as Matt's PBR project. That function actually does the "camera work", and saves the result in a Texture. This is done on the fly during level transition.
     
     
    Skipping Dynamic Objects
     
    There is just one problem however. Everything is being rendered when the cubemaps are being built. So any enemies, or move-able objects will be baked into a reflection. To fix this, we need to have all objects that can move (Mostly have mass) be hidden, and then re-shown after we've finished the building of cubemaps.
     
    Pretty much.
     

    #define S_NULL ""
    #define ON "1"
     
    void ShowMoveables(Entity* entity)
    {
    if (entity != NULL)
    {
     
    if (entity->Hidden() == true && entity->GetKeyValue("hide_for_cubemap", S_NULL) == ON)
    {
    entity->Show();
    entity->AddForce(Vec3(0, 0, 0)); // <-Wake me!
    entity->SetKeyValue("hide_for_cubemap", S_NULL);
    }
    }
    }
     
    void HideMoveables(Entity* entity)
    {
    if (entity != NULL)
    {
    // Things to look for ar entites with the mass > 0, or have their shadow mode non static!
    if (entity->GetMass() == 0 || entity->GetShadowMode() == Light::Static)
    return;
     
    // Only hide the non-hidden!
    if (entity->Hidden() == false)
    {
    entity->SetKeyValue("hide_for_cubemap", ON);
    entity->Hide();
    }
    }
    }
     
    void MapHook(Entity* entity, Object* extra)
    {
    // For cubemap generation, we don't want anything that's set to move to be built in with our cubemaps.
    HideMoveables(entity);
    }
     
     
    bool WorldManager::Connect(std::string pMapname)
    {
    ...
     
    if (Map::Load(pMapname, &MapHook) == false)
    return false;
    ...
    return true;
    }
     
     
    //--------------------------------------
    // For cubemap building!
    //--------------------------------------
    void WorldManager::MakeMoveablesVisible()
    {
    vector<Entity*>::iterator iter = entities.begin();
    for (iter; iter != entities.end(); iter++)
    {
    Entity* entity = *iter;
    ShowMoveables(entity);
    }
    }

     
    Then back in the initialize function of our cubemap factory before we return true.
     

     
    // Lastly, redraw moveables!
    worldmanager.MakeMoveablesVisible();

     
    And now anything that has mass, or has it's ShadowMode set to Dynamic will not be baked into the reflection!
     
     
    Cleaning up
     
    To make it so that this can work with multiple level loads and when the game "disconnects" (Not in a map), we need to reset everything.
     
    Cubemap factory:
     

     
    void CubemapFactory::Render()
    {
    if (WORLD != NULL || worldmanager.IsConnected() == true)
    {
    if (!CubemapFactory::init) { CubemapFactory::initialize(); }
    WORLD->Render();
    }
    }
     
    void CubemapFactory::Release()
    {
    System::Print("Clearing Cubemaps!");
    CubemapFactory::init = false;
    if (cubeTexture != NULL)
    {
    cubeTexture->Release();
    }
    CubemapFactory::currentContext = nullptr;
    CubemapFactory::reflectionCamera = nullptr;
    CubemapFactory::cubeBuffer = nullptr;
    CubemapFactory::cubeTexture = nullptr;
    }

     
    And instead of calling world->Clear in my world manager, I instead call this function:
     

     
    void WorldManager::ClearWorld()
    {
    CubemapFactory::Release();
    world->Clear();
    }

     
    Calling CubemapFactory::Release() may not be necessary as it's called everytime the factory is initialized, but it doesn't hurt to be safe!
     
     
    Conclusion
     
    It was a ride getting this to work correctly. I'm still not happy about how the cubemaps look for near by entities, but it's better than what we have now, which is nothing, Apperently, Josh is concidering this as a engine feature so all of this work might just be temp until he adds it in, which hopefully will be smoother and less error prone!
     
     
    But what we have a system that works like this:
    Engine loads a map, world created, etc.
     
    A map hook hides any entities that has mass or a dynamic shadow mode.
     
    Cubemap Factory takes it's "pictures" much like Source does when buildcubemaps is executed.
     
    Each cubemap texture is given to the right Cubemap entity, then released.
     
    The cubemap looks for near by entites and swaps the model surface's default cubemap with the one it got from the factory.
     
    Cubemap factory tells the world manager to re-show the entities it hid.
    Things continue as normal.

     


     
     
    So that's it! Thanks for reading!
  4. reepblue
    After the release of the Vectronic Demo, I started to seek out how to make the game to be able to stand-alone without any additional launchers, batches, or required installations. Shortly after my last blog post, Josh informed me about Inno Setup, and it answered my questions about providing OpenAL if the user did not already have it installed. More about that here. I haven't played with it yet, but it gave me insurance about packaging it up for the final release down the road. Alternatively, Steamworks is also an option, but I'm not touching anything Steamworks related until I get a App ID for the game.
     
    With the Leadwerks Game Launcher, users can edit basic settings such as "Resolution" and "Fullscreen" and any settings that may come in the future. As a stand alone game, it would not have easy to use game launcher, hence a menu with a settings feature is required.
     
    I didn't want to make a launcher, as they are kind of unpopular, and OS exclusive. (You'll have to build a launcher for each platform.) I wanted something already in the application, and can be compiled on multiple platforms minus a few edits here and there. I also wanted most settings to be real time so players can adjust their settings to their likings without needing to restart the game.
     

     
    One thing I really liked about developing mods for the older branch of Source was the gameui system it had. It was simple, reusable, and out of the way. You could edit the res files to customize it if you wish, but it's stock files was enough to get you started; even release with. I wanted my UI to be based on this. Only reason why they retired this UI system in favor of their hell of a client based UI system starting with Left 4 Dead, (I played with it, it was not fun) was due to Console/Controller support. Yes, the Xbox360 controller does work in the old UI, but when you open it's panels, you better have a mouse near by!
     
    However, with things like the Steam Controller, and the fact that Leadwerks is mostly PC based anyway, this UI design will certainly do.
     
    To make it reusable, the UI would have be modified outside of the source code. Changing fonts, colors, titles should not require a recompile. Also, such things like the start map, and background map should also be able to be changed outside of the source. No matter what game it is, it should work.
     
    Finally, to make it out of the way, the entire system would be written in C++, and not interfere with any game logic written in Lua. However, from Lua scripts, you should be able to communicate with it in case you wish the application to disconnect you from a map, or shutdown the application entirely. From lua, you should also be able to grab boolean states like if the app is paused, connected (in-map playing) or disconnected, and not in any map if needed.
     
    So that was the goal, and 3-4 weeks later, the goal was completed with a few bonuses such as a precache list for loading models/prefabs onto memory, a very basic save system, and in dev mode (which is also enabled in debug mode), information about what version of the engine you're running and map name is printed in the top right corner when paused. (Something Source does which I liked.)
     

     

     

    (Could not use media tags due to them being Unlisted)
     
    I'm very happy of the results. Not only this necessary element for a stand alone is done, but this will make my testers happy as they no longer need to edit batch files to configure their settings! I believe that this is necessary, and anyone should be able to have it. After a few more bug fixes, I'll upload the Source files to GitHub along with application releases so even people with the indie version can use it.
     
    Only thing I did not test was the application on Linux since the hard drive in my Linux machine finally died after 7 years of service. Knew it was coming, but still a set back. Hence another reason why I want to make it public, so that it works on all supported platforms and gets more optimized over time. I'm not the best programmer in the world, so every little thing helps!
     
    I'll talk more about this when I put it up. There is a lot of technical stuff about it,
  5. reepblue
    Crawler's Den is a demo I made for the Summer Games Tournament this year. You can grab it here. This blog I'd like to share my reflections on developing the project on LEX2 and Leadwerks in general.
     
    When I started the project, my template had no animation manger, and the puppeteer system was just coming online. The goal was to recreate the AI and Events map and write the entire thing in C++. Not only to test the puppeteer system and my ideas on how to link said puppeteers to the flowgraph, but I would have classes to derive from when I start working on Vectronic again.
     
    Right now, LEX2 is a template that gets installed in the Templates folder of the Leadwerks App Directory. However, upon creating a template, I noticed that the project and sources didn't seem to copy over as they did in testing. I think it has to do with the project manager's date based system as I was on the beta branch when I started. So fixing my project was no biggie, but not something I'd like to ship out.
     
    The player was the first thing to get coded as I wrote about here. I based my player code of the Source engine's player because I really like how that feels. It wasn't long until I had a player that can walk, jump, crouch, and use/pickup objects. Neat! I'm really happy how it turned out. There are some improvements I thought I can make, but it was drawing close to the deadline so it has to wait.
     
    The animation system was next. Without it, no gun and no crawlers. I started transferring code line by line but I didn't understand how to convert for inpairs loops to C++. This is when CrazyCarpet got involved as he was very willing to help as he needed an animation system for his stuff too. It took a few days to iron out, and minus slight problems I'm having, it works pretty much the same as it's lua variant. You can read the topic here about it which includes a link to the final code on Aggror's Wiki.
     
    Now having the animation system in place, the weapon was next. I first translated the FPSGun script from 3.2 as it was a lot simpler and it was something to get started. I later added bullets, and allowance of different weapon types although the project would just need a pistol.
     
    The crawler was something I put on the backburner as I never did AI in Leadwerks before. I was aware of the functions, but never needed to use them. This was my first project where I needed to use the navmesh system too. I did level design next because I really wanted to start mapping.
     
    I needed to start from scratch with the map. The AI and Events map used meters instead of centimeters so nothing lined up with the grid anymore. there was once an option to switch between the two measurements, but I guess Josh took it out, or it's under legacy features. Ether way, I wanted to use centimeters. I shelled the map out by eye and feel, I hardly went back to the source material as that would require switching the project and such. I had the map mostly shelled out before I started to work on textures.
     
    I usually use Paint.Net for raster artwork, but I decided for this project to use an older version of Photoshop. Not only because it's "what the pros use" but I want to use a file format that could be readable in other software. PSDs can be read by gimp so if there comes a day in which I'm making games on a non-windows machine, I can still open psds. I should learn how to use gimp and ditch Paint.net as PDN is Windows only. But I wanted to make a game, not learn new software.
     
    Now that I know how to properly port in models from blender, I had a lot of fun modeling for my scene. Making support beams, trims vents, and such was really fun, and everything got imported correctly. I had the best luck using FBX ASCII 6.2 versus the Binary 7.4 option, and Applying scale made sure that everything was sized correctly with a default scale setting as 1,1,1 like it should be. I couldn't remodel everything, I just remodeled things that wouldn't connect together right due to the measurement change. There was some old models I just used the model editors scale tool.
     
    While building my level was really fun, optimizing was the exact opposite. I limited my shadows, optimized my prop settings, made lights smaller, and collapsed a lot of modular pieces together and with my 750Ti, I get 90fps in the first room and 70ish in the showdown room. One thing I always thought was a good idea was to develop on medium range hardware. If you were to develop on lets say a 980Ti, you could get away with a few more expenses, but throw it on an older card, and it might chug, Develop on a mid-range card, and it'll play fine on that, better on newer and powerful hardware. I was cutting batches and polys like crazy but I couldn't get to a point where the entire scene was 100fps. I've added a wireframe view mode to my console options to see what was being drawn that shouldn't in which made me develop a portal volume that could hide, or show all the objects in it's volume while ignoring collapsed brushes.
     

     
    I eventually ditched the portal volumes because of a bug that I didn't feel like fixing, and I only saw 5 more frames in the end which wasn't worth the flowgraph mess I had to do. If I could get it to be automatic and fix that bug, I might re-add it.
     
    There was a time when I sent the project to CrazyCarpet to debug something and this is when I learned that my theory about developing on mid-range hardware was correct. He reported 100-120+ frames on his 980, and for some reason was getting better results on his 750. My PC is no spring chicken, but he was getting 100 frames all round on a 750, meanwhile I drop to 70 on my 750Ti?
     
    While I'm really happy that my pain and suffering resulted in better performance on newer hardware, it'd be great if I could have those results on my end.
     
    The deadline was drawing near, and it was time to do the crawler, and a few other classes. By this time, I've developed and successfully created a way for my puppeteer class to communicate with the flowgraph. In short, each puppeteer has it's own lua script in which you can set the values and write the inputs and outputs to the class. Inputs are handled through a message system while outputs are fired by the entitles component class. I'd have to write another blog on how it works but I'm sure weeks after I do, there might be an official way to link C++ classes to your game and the flowgraph. So I played with that converting more scripts to C++. Then it came time for the crawler.
     
    The crawler was a bit more difficult, and this is when I noticed an issue with the animation manager. It wasn't playing looping animations correctly. A quick line comment of --n fixed this, but to this day there is a problem in which it has a hard time going from attacking to running. It defaults back to it's idle animation and then glides around. Might be my monster code, but I was going on the last few days and the map wasn't done yet.
     
    I was using the beta branch for a while, then Josh pushed a broken build which put me back on 4.1. I noticed the editor runs more smoothly on the default branch than the beta branch. I would think that this has to do with a debug settings or garbage collection that's enabled when Josh does development and only turns it off during prime time. But then, the beta branch was "fixed" so I jumped to that while keeping my game on 4.1 (I archive releases). I noticed a bug with the probes, and my map got corrupted on it. Thankfully I made a backup, so I didn't lose everything. I went back to the speedy stable release, finished my map and went to upload it.
     
    Uploading is always a chore on my connection. I really hate when I spend hours uploading something and then after all that it fails to upload due to some error. This is why the Vectronic Demo is out of date on the game launcher, and why Crawler's Den is only on my dropbox right now. I should have used my mobile data, but I didn't for some reason.
     
    Ether way, besides a few small bugs, the project is out and playable using LEX2. Here is a summary of working with it.
     
    Good:
    No worry about back end stuff, besides somethings I needed to improve thanks to this project, I didn't have to spend a week writing window, context or world code.
    Console was a god send allowing me to easily debug my map in-game. Loading whatever map I want without the editor or hard coding was pretty dope.
    Once they were done, the puppeteers are really neat to work with. They are set up like lua scripts, and all the hooking is done in the base class.
    The precaching and Asset Manager worked amazingly well. No more slowdowns on loading models in real time, and no crashes because you mistyped the path. Really handy!
    I made a volume class for triggers and such. Works way better than collision based ones.

     
    Bad:
    Project's don't copy right with the project manager. Again, might be the date based system.
    The input system isn't done. It uses SDL for it's input handling and there is a bug in which all inputs must be done in UpdateWorld. CrazyCarpet said he'll look at this so we can correctly place movement inputs in UpdatePhysics.
    I feel as though Puppeteers should be called actors. They are called puppeteers because there was a class called Actor which was the actual entity doing the actions instead of a separate class controlling the entity.
    A lot of commented out code. I need to delete some things I've commented out months ago.
    It was kind of a pain to make sure the core code was synced up with the base project, the game project and the git repo. I want it so that all games off of LEX2 use the same Core files.
    Some reason I couldn't derive from my volume triggers....

     
    I have an idea for this going forward but I don't want to express it just yet. No matter how I go forward, the foundation is here to stay. It might get cleaned up, maybe edited a lot, but I don't see a need for a rewrite, it's all solid.
     
    Thank you for reading this, I hope you have fun with Crawler's Den!
  6. reepblue
    There has been some discussion regarding on how to set collision shapes for your models. For 95% of models, you should be building shapes with the Model Viewer as described here. In some cases, the model artist might want a custom shape to be made. In this post, I'll be going over how I import models into Leadwerks, and building custom shapes.
    A few notes first. I use Blender; Blender 2.79b to be exact. I haven't get got the hang of 2.80 and until the new engine's art pipeline is fully online, I don't see a use for it. Leadwerks 4 uses Blinn-Phong rendering so the PBR stuff makes no sense for Leadwerks 4. So for this, I'll be posting screenshots from 2.79b. I should also mentioned that a feature I use in my process isn't present in the Linux build of the editor, which is the collapse tool. (Tools->Collapse). Doing the collapsing via a terminal will cause the models to crash the editor. This seems to be a known bug, as you don't see that feature in the Linux editor.
    Lets say you created a tube model such as this one and you want the player and objects to go into the tube:

    If you tried to make a shape doing the Concave settings, not only it'll be really slow to generate, but the results will not be good. We could make a shape based on the wire frame, but this is a high poly model. What we need to do is make a new mesh, import both models  to the editor, collapse them both, build the shapes for both, and delete the low poly model while making the high poly read the low poly's generated shape.
     
    First to get it out of the way, apply the scale and rotation of the model. This will make Y forward and the scale will be (1,1,1) when you import it into Leadwerks.

     
    Next we need a low poly model.

    This is the same proportions as our high poly. Apply the scale and rotation as the same as the high poly. I also set the max draw time to solid, but this is optional.

    Next, name your High poly and the low poly you're going to be using for the shape appropriately.

    Now lets, export each object as a FBX. For this my high poly is going out as tube.fbx, and my low poly shape is going out as tubeshape.fbx. Here are my export settings:

    If you saved the files in a Leadwerks project while the editor was opened, the editor would have auto convert the files to the .mdl file format. Open the high poly model (tube.fbx) and first collapse it and give it any shape. (Give it a box shape to save time.) you need to assign a shape to the high poly so the mdl file is linked to a phys file. Do the same with the low poly, but you're gonna set the shape as poly mesh.


    Close the model viewer, and then go into the directory where the models are placed. We are now going to delete the box shape of our high poly, and trick it into loading the low poly shape by renaming the shape file of the low poly to be what the previous shape of the high poly was. In other words, we are making tubeshape.phy into tube.phy.
    Before:

    After:

    Notice the time stamp and the size of tubeshape.phy from before being the same as tube.phy in the after screen cap. This should be your end result.

    Notice that the shape isn't sold but now a tube. Objects can go into the tube with no issues. Now, there is another way that uses limb names to generate physics automatically. However, there are a lot of issues I came across using this method such as the shape not being parented to the model when the model moved via physics or a joint. With this way, you have a custom shape, the model is optimized because it doesn't have any children nodes, and everything is clean and tidy!

     
  7. reepblue
    If you join the beta branch and updated your projects, you'd probably noticed that the low res Leadwerks 2 icons have now been replaced with high resolution ones that match the rest of the user interface. Decals, Particles Emitters, Cameras, and Probes now have their own icon, and you can tell all entities apart just by looking at them.
     
    I've originally made these a while ago when I noticed that each entity had it's own material and texture, but most of them just recycled the pivot icon. I've released a version on the workshop, and an updated version as part of the LEX template. Yesterday, Josh contacted me about using them in the official build, and I was more than happy to say yes, and make any wanted changes. He mostly wanted the lights to be yellow, and I've changed the angle of the spotlight to be more downward.
     
    Each icon texture is 512x512 uncompressed, and was made using a vector art program so the icons are sharp and clear as possible. These icons will be skipped during the packaging process, so they will not bloat your final game.
     
    You can get these icons by joining the beta branch and updating your project. These icons will be released as part of Leadwerks 4.1. If you think anything needs to be changed, let ether Josh or I know.
     
    I think the only thing left to do is update the light buttons on the side, really. Here's a mock up of what it can look like. To be honest, I think they kind of clash with the other icons as those where shipped with 3.0 which had some sort of a Windows 7 icon pack for everything else.
     

  8. reepblue
    Over 2 weeks ago, I released a small demo showcasing my project's core gameplay on the Leadwerks Engine. Although I'm happy with the demo; it's a good landmark for the project, but it can be better visually and functionality. I learned a lot of new information about the engine on how to better optimize it, multiplatform diffrences, and I grew a slight hatred to a certain brand of GPUs. But the demo on the Game Launcher to me was a success.
     
    The stand-alone version in my opinion was a bit shakey as with stand-alone releases you lose the end user's ease of use when it comes to settings and the requirement of OpenAL. The Vectronic Demo was indeed targeted to work with the Game Launcher from the begining as I wanted to be more focused on the actual game scripting than the dirty stuff such as how settings are loaded, menus, etc. But I also wanted a stand-alone version out due to the launcher being in beta, and I wanted to let people on linux play it.
     
    I took Vectronic to the Portal community to see how they liked it. I mostly got back issues in actually launching the application. Besides the lack of OpenAL, most people did not seem to understand how they can run the Portal 2 (Which runs on a branch of the Source Engine that can run on DX9 cards.), and other games but not Leadwerks; especally on a certain brand of a GPU. One person actually got angry at me because he could not run the game. Overall, it was mostly things that Josh nor I can fix. (i.e it was ether outdated hardware or poor drivers.) However, the OpenAL requirement is something I can look into.
     
    Right now, I'm stepping away from the demo, and focusing on improving the stand-alone experience now since the soultions for the standard edition has been recently optimized. With the Summer Games Tournament coming up, it's a good deadline to stay focused, and to experment with non-Vectronic stuff. After this project is done, Vectronic will be back on track as I kind of need this done before I continue to work on my main project.
     
    I still got Part 2 of My Introduction Entry to write which I share why and how I moved the Project to Leadwerks, but I think I'll write that when I want to look back as right now I want to looking forward. I might also share various techniques I used in the Vectronic Demo as I think it will help with any user of Leadwerks; especially durning the summer tournament. I'll continue to write about whatever I'm working on here.
  9. reepblue
    I wanted to give you an update since this blog post and let you know how well (or not) I'm following those plans.
     
    Blue Portals: Anniversary Edition (Skip this if you don't care..)
     
    Sunday I pushed a long over due push to Steam for beta testers to play and give feedback. I don't like to push to Steam often as my connection isn't that strong so any uploads bigger then a few megabytes causes an upload failure, I usually split updates into multiple pushes to prevent such problems but sometimes you have to push one large file that'll cause a error. This time I instead used my unlimited 4G mobile data and I had way better luck, and will be using this method from now on.
     
    We right now only have a small handful of testers and I wish to keep it that way for a little while longer. It's interesting to see the same play-tester play the same map in different updates regardless if anything changed. There was one map where no reports or issues where given from this tester last update. I barely changed the room, and this time they informed me that something felt impossible to achieve.
     
    I also keep play-testers out of developer news. This way I can introduce something brand new to them and they can report back if they understood it and/or liked the idea. I really wish I was in a position to watch people using Steam's broadcasting service, but I think even with my mobile data, it'd be unwatchable due to the high ping that wireless services have.
     
    Although I wished the teaser trailer was done last month, I'm happy to report that it's being worked on now. I've got a message this morning that the recording is done. It just needs to be edited and such. Really cool as the mod has a look and feel that we're happy with. I guess it's better that the stuff got recorded today than a month ago as a month ago some models still had their ambient occlusion maps for skins!
     
    Overall, the mod is coming together very nicely. There are a few things I'm worried about but I'm sure time will allow me to answer more questions I have. I'll keep you posted on this as I see it as a major stepping stone.
     
    LEX2
     
    A few hours ago, I pushed a near final build of it. In-short, I've cleaned up the code, deleted useless classes and changed how somethings operate.
     
    For example, LEX2 has this function called AssetManager:PrecacheGlobal(path) which you call when you want something to be pre-loaded in memory so your game can load it later without a hitch. This was done by calling AssetManager:PrecacheGlobal(path) in App:Start() and what it did was load those paths into a list. Then when ever the world loaded a new map, PrecacheList was called which loaded every asset in that list. Although you only needed to define the asset once, it still needed to be loaded multiple times!
     
    In this blog, Josh mentioned that it's possible to load assets into one world and have it just sit in memory while your game world is active. I decided to try this, but I was careful to prevent two worlds from being current at the same time.
     
    First, I retired the old PrecacheList and made a new function.
     

    void AssetManager::PrecacheJunkyard()
    {
    DevMsg("AssetManager: Creating Junkyard.");
     
    // Save the current world if we have one.
    World* oldworld = WORLD;
    junkyard = World::Create();
    World::SetCurrent(junkyard);
     
    // Precache global objects.
    std::list<std::string>::iterator it;
    for (it = precache_list.begin(); it != precache_list.end(); ++it)
    {
    if (it->c_str() != S_NULL)
    {
    AssetManager::Precache(it->c_str());
    }
    }
     
    // Go back to the 'real' world if there was one.
    if (oldworld != NULL)
    {
    World::SetCurrent(oldworld);
    }
    else
    {
    World::SetCurrent(NULL);
    }
     
    }

     
    This will load all the assets in the list that PreacheList used into a different world than the game. Then if there is ever the chance that a world exists when the junkyard world is created, it'll go back to it. To prevent that ever happening though, I made this function not exposed to Lua and have it called at the very end of App::Start() in C++.
     

    bool App::Start()  
    {
    //Create a window
    windowmanager.CreateWindow(System::AppName);
     
    // Create a context
    contextmanager.CreateContext();
     
    ...
     
     
    #ifdef LUA_GAME
    // Finally, test the app script
    if (ScriptApp::Start() == false)
    {
    #ifdef WIN32
    MessageBox(NULL, "Error: Failed to execute App script!", "Critical Error!", MB_OK | MB_IConstop); //MB_ICONWARNING
    #endif
    return false;
    }
    #else
    if (GAME::Start() == false)
    {
    return false;
    }
    #endif
     
    ...
     
    // If the user preached anything in App:Start() or GAME::Start(), load them in another world.
    // The Junkyard world will keep those entities in memory.
    AssetManager::PrecacheJunkyard();
     
    ...
     
    return true;
    }

     
    So then your Lua script will look like this
     

    function App:Start() AssetManager:PrecacheGlobal("Models/Characters/hitbox.mdl") AssetManager:PrecacheGlobal("Models/Junk/cinderblock.mdl") return true end
     
    But wait! What if you want the game to automatically launch a map? I didn't want a conflict where a world would exist, and then another world gets created to spawn assets. For safety measures, I just made a PostStart() function for Lua, and have it called after AssetManager::PrecacheJunkyard().
     

    {
    .....
     
    // If the user preached anything in App:Start() or GAME::Start(), load them in another world.
    // The Junkyard world will keep those entities in memory.
    AssetManager::PrecacheJunkyard();
     
    #ifdef LUA_GAME
    ScriptApp::PostStart();
    #else
    GAME::PostStart();
    #endif
     
    return true;
    }

     

    -- This function will be called right after when the program starts. function App:PostStart() end
     
    I really didn't test what would happen if I did load a map before AssetManager::PrecacheJunkyard(). I assume there would be a pause, as the worlds get swapped around, but I'm a bit too scared to try.. but also kind of curious...
     
    I've also sorted the filesytem. Everything LEX2 is within Source/Core with one cpp file looking like this.
     

    // $PROJECT_NAME_main.cpp.
    #include "Core/base.h"
     
    #ifndef LUA_GAME
    namespace GAME
    {
    // This function will be called once when the program starts.
    bool Start()
    {
    return true;
    }
     
    // This function will be called right after when the program starts.
    void PostStart()
    {
    }
     
    // This is our main program loop and will be called continuously until the program ends.
    bool Loop()
    {
    return true;
    }
     
    // This function is called when the program is loading a map. (This is where you draw your loading screen!)
    void OnMapLoad()
    {
    }
     
    // This function is called when the program is called to pause.
    void onpause()
    {
    }
     
    // This function is called when the program is called to resume.
    void onresume()
    {
    }
     
    // This function is called when the program finished loading a map.
    void OnPostMapLoad()
    {
    }
     
    // This function is called when the program disconnected from a map.
    void OnDisconnect()
    {
    }
     
    // This function is called when the program closes.
    void OnShutdown()
    {
    }
     
    // C++ function only. This is how you link editor entities to your C++ classes
    void LoadEntity(Entity* entity)
    {
    /*
    // Attach loaded entites to puppeteers.
    // This can get long and messy, there is some talk about this getting auto-generated with an offical actor class.
    // Cross your figners for that day....
    Puppeteer* puppeteer = NULL;
    if (entity->GetKeyValue("puppeteer") == "Rocket")
    {
    puppeteer = new Rocket(entity);
    }
    else if (entity->GetKeyValue("puppeteer") =="Demon")
    {
    puppeteer = new Demon(entity);
    }
    else if (entity->GetKeyValue("puppeteer") =="Portal")
    {
    puppeteer = new Portal(entity);
    }
    */
    }
    }
    #endif // !LUA_GAME

     
    As you can see, it's pretty much exactly like App.lua. If LUA_GAME isn't defined, it calls this code instead of the App.lua file. I did this to make making a game for C++ and Lua very similar. There is a function that gets called within the MapHook to link entities to the Puppeteer class which can be linked with one script.
     

    Script.puppeteername="" --choiceedit "Puppeteer" "foo,boo" function Script:Start() -- Attach us to a C++ class like this! self.entity:SetKeyValue("puppeteer",puppeteername) end -- There needs to be a better way of flowgraphing with C++ classes!! function Script:User1()--in self.component:CallOutputs("User1") end function Script:User2()--in self.component:CallOutputs("User2") end function Script:User3()--in self.component:CallOutputs("User3") end function Script:User4()--in self.component:CallOutputs("User4") end
     
     
    Summer Games Tournament
     
    As I stated, I much want to participate in this one. I don't really have much time to make a unique piece, but I want something to stress test LEX2 and see if it can be used easily and effectively. I'm thinking about my take on the AI and Events map from the Advanced First-Person Shooter Template. That map kind of bothers me as the scaling is all wacky (It was made when the editor used Meters) and I feel that it's in the one person team field of difficulty.
     
    Seems easy, where's the challenge? Well, While the end result will be a revision, I want to remodel most assets and code the entire game in C++ using LEX2's puppeteer system! I also will be coding things to my preferences so it's not like I'm just gonna convert existing Lua code to C++.
     
    I had other ideas but this seems like the best one currently. It should be an interesting project, and I'll open-source it when I'm done. Anything I make will be available for others to use in the future. I wish to start tomorrow; or when I wake up as it's past midnight.
     
     
    Steam Dev Days
     
    I'm making arrangements to go to Steam Dev Days this year. I've bought my ticket and I just got a few things to iron out with traveling, hotel, and some company naming conflict. My overall goal is to talk to network with other people, talk about developing games, and learn new things. I'm really stoked about the entire thing.
     
     
    That's all for now, wish me luck!
  10. reepblue
    For some reason, I've been seeing a lot of questions on how to add actors created into C++ recently. My first attempt on this was in 2016 with Crawler's Den; A modern remake of the SDK example level. That was on a version of Leadwerks in which there was no official Actor system in place. Today, the engine has an Actor class which can be attached to any entity, but besides some loose examples, it's not well documented. Also there is no official way on linking your entities in the editor with your actors. But today I wish to share with you some insight on how I managed to get not only actors attaching in my maps, but also allowing interaction with the flowgraph system.
    Disclaimer: This is more copy and paste code only tested with Leadwerks 4.
    Base Actor And Actor Factory
    First, you'll want to make a base class off the engine's Actor class that'll allow us to easily grab values from the lua file. Yes, we can have an Actor and a Script attached to the same entity at a time! The idea is that the lua script will be our definition file for our actor to load on map load.
    #ifndef BASEACTOR_H #define BASEACTOR_H #if defined( _WIN32 ) #pragma once #endif #include "stdafx.h" #define ACTOR_KEYVALUE "classname" class BaseActor : public Actor { public: void SetBoolValue(const std::string& pValue, const bool pDefault = false); bool GetBoolValue(const std::string& pValue, const bool pDefault = false); void SetFloatValue(const std::string& pValue, const float pDefault = 0); float GetFloatValue(const std::string& pValue, const float pDefault = 0); void SetIntValue(const std::string& pValue, const int pDefault = 0); int GetIntValue(const std::string& pValue, const int pDefault = 0); void SetStringValue(const std::string& pValue, const std::string& pDefault = ""); std::string GetStringValue(const std::string& pValue, const std::string& pDefault = ""); Vec2* GetVec2Value(const std::string& pValue, Vec2* pDefault = 0); Vec3* GetVec3Value(const std::string& pValue, Vec3* pDefault = 0); Vec4* GetVec4Value(const std::string& pValue, Vec4* pDefault = 0); Entity* GetEntityValue(const std::string& pValue, Entity* pDefault = NULL); void FireOutput(const std::string& pEvent); }; #endif #include "stdafx.h" #include "baseactor.h" //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void BaseActor::SetBoolValue(const std::string& pValue, const bool pDefault) { #ifndef LEADWERKS_5 GetEntity()->SetBool(pValue, pDefault); #else GetEntity()->SetBoolean(pValue, pDefault); #endif } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- bool BaseActor::GetBoolValue(const std::string& pValue, const bool pDefault) { if (GetEntity() == nullptr) return pDefault; #ifndef LEADWERKS_5 if (entity->GetBool(pValue) != pDefault) { return entity->GetBool(pValue); } #else if (GetEntity()->GetBoolean(pValue) != pDefault) { return GetEntity()->GetBoolean(pValue); } #endif return pDefault; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void BaseActor::SetFloatValue(const std::string& pValue, const float pDefault) { #ifndef LEADWERKS_5 GetEntity()->SetFloat(pValue, pDefault); #else GetEntity()->SetNumber(pValue, (float)pDefault); #endif } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- float BaseActor::GetFloatValue(const std::string& pValue, const float pDefault) { if (GetEntity() == nullptr) return pDefault; #ifndef LEADWERKS_5 if (entity->GetFloat(pValue) != NULL) { return entity->GetFloat(pValue); } #else if (GetEntity()->GetNumber(pValue) != NULL) { return (float)GetEntity()->GetNumber(pValue); } #endif return pDefault; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void BaseActor::SetIntValue(const std::string& pValue, const int pDefault) { GetEntity()->SetString(pValue, to_string(pDefault)); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- int BaseActor::GetIntValue(const std::string& pValue, const int pDefault) { if (GetEntity() == nullptr) return pDefault; #ifndef LEADWERKS_5 if (entity->GetFloat(pValue) != NULL) { return static_cast<int>(entity->GetFloat(pValue)); } #else if (GetEntity()->GetNumber(pValue) != NULL) { double x = GetEntity()->GetNumber(pValue); // stored as 54.999999... x = x + 0.5 - (x < 0); // x is now 55.499999... int y = (int)x; // truncated return y; } #endif return pDefault; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void BaseActor::SetStringValue(const std::string& pValue, const std::string& pDefault) { GetEntity()->SetString(pValue, pDefault); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- std::string BaseActor::GetStringValue(const std::string& pValue, const std::string& pDefault) { if (GetEntity() == nullptr) return pDefault; #ifndef LEADWERKS_5 if (entity->GetString(pValue) != "") { return entity->GetString(pValue); } #else if (GetEntity()->GetString(pValue) != "") { return GetEntity()->GetString(pValue); } #endif return pDefault; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- Vec2* BaseActor::GetVec2Value(const std::string& pValue, Vec2* pDefault) { if (GetEntity() == nullptr) return pDefault; #ifndef LEADWERKS_5 Vec2* test = static_cast<Vec2*>(entity->GetObject(pValue)); if (test != NULL) { return test; } #else Vec2* test = (Vec2*)GetEntity()->GetObjectPointer(pValue); if (test != NULL) { return test; } #endif return pDefault; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- Vec3* BaseActor::GetVec3Value(const std::string& pValue, Vec3* pDefault) { if (GetEntity() == nullptr) return pDefault; #ifndef LEADWERKS_5 Vec3* test = static_cast<Vec3*>(entity->GetObject(pValue)); if (test != NULL) { return test; } #else Vec3* test = (Vec3*)GetEntity()->GetObjectPointer(pValue); if (test != NULL) { return test; } #endif return pDefault; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- Vec4* BaseActor::GetVec4Value(const std::string& pValue, Vec4* pDefault) { if (GetEntity() == nullptr) return pDefault; #ifndef LEADWERKS_5 Vec4* test = static_cast<Vec4*>(entity->GetObject(pValue)); if (test != NULL) { return test; } #else Vec4* test = (Vec4*)GetEntity()->GetObjectPointer(pValue); if (test != NULL) { return test; } #endif return pDefault; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- Entity* BaseActor::GetEntityValue(const std::string& pValue, Entity* pDefault) { if (GetEntity() == nullptr) return pDefault; #ifndef LEADWERKS_5 Entity* test = static_cast<Entity*>(entity->GetObject(pValue)); if (test != NULL) { return test; } #else Entity* test = (Entity*)GetEntity()->GetObjectPointer(pValue); if (test != NULL) { return test; } #endif return pDefault; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void BaseActor::FireOutput(const std::string& pEvent) { if (GetEntity() == NULL) return; #ifndef LEADWERKS_5 if (entity->component != NULL) { entity->component->CallOutputs(pEvent); } #else GetEntity()->FireOutputs(pEvent); #endif } Now we need a actor factory of some kind. There is probably an easier and modern way of doing this, but this is how I do it. First we create a header file called actorfactory.h
    #ifndef ACTORFACTORY_H #define ACTORFACTORY_H #if defined( _WIN32 ) #pragma once #endif #include "stdafx.h" extern void BaseActorFactory(Entity* pEntity, Object* pObject); #endif // ACTORFACTORY_H Then in actorfactory.cpp, I have the following. 
    #include "stdafx.h" #include "baseweapon.h" #include "clientcamera.h" #include "doors.h" #include "noise.h" #include "fpsplayer.h" #include "logicactors.h" #include "pointmessage.h" #include "propspawner.h" #include "point_transition.h" #include "platform.h" #include "triggers.h" #include "vrplayer.h" #define ATTACH_NAME_TO_ACTOR(_name_, _actor_) if (test == _name_) actor = new _actor_() void BaseActorFactory(Entity * pEntity, Object * pObject) { auto classname = GetEntityKeyValue(pEntity, ACTOR_KEYVALUE); if (classname != "") { BaseActor* actor = NULL; std::string entname = pEntity->GetKeyValue("name", "STATIC_MESH"); std::string test = String::Lower(classname); // Global actor is a dummy actor with values for other actors. if (test == "global") { if (g_mGlobalActor == NULL) { actor = new BaseActor(); g_mGlobalActor = actor; } } ATTACH_NAME_TO_ACTOR("ambient_generic", AmbientGeneric); ATTACH_NAME_TO_ACTOR("door_sliding", SlidingDoor); ATTACH_NAME_TO_ACTOR("door_rotating", RotatingDoor); ATTACH_NAME_TO_ACTOR("point_path", PointPath); ATTACH_NAME_TO_ACTOR("point_message", PointMessage); ATTACH_NAME_TO_ACTOR("point_transition", PointTransition); ATTACH_NAME_TO_ACTOR("point_weapon_pickup", PointWeaponPickup); ATTACH_NAME_TO_ACTOR("prop_spawner", PropSpawner); ATTACH_NAME_TO_ACTOR("logic_relay", LogicRelay); ATTACH_NAME_TO_ACTOR("logic_branch", LogicBranch); ATTACH_NAME_TO_ACTOR("logic_counter", LogicCounter); ATTACH_NAME_TO_ACTOR("train", TrainActor); ATTACH_NAME_TO_ACTOR("trigger_once", CollisionTrigger); ATTACH_NAME_TO_ACTOR("trigger_multiple", CollisionTriggerMultiple); ATTACH_NAME_TO_ACTOR("volume", BaseVolume); ATTACH_NAME_TO_ACTOR("volume_push", PushVolume); ATTACH_NAME_TO_ACTOR("volume_hurt", HurtVolume); ATTACH_NAME_TO_ACTOR("weapon_pickup", WeaponPickup); if (BaseActor::AttachActor(pEntity, actor, true)) { pEntity->SetKeyValue(ACTOR_KEYVALUE, classname); System::Print("Attached actor: \"" + classname + "\" to \"" + entname + "\"."); } else { System::Print("Error: failed to attach actor: \"" + classname + "\" to \"" + entname + "\"."); } } } Each actor gets a "classname" assigned to it. This can be anything you want and it's defined here. I went with a quake naming scheme for personal preference.
    We then call this function in our map load hook as so. This will pass each entity loaded though our factory when the map file is being loaded in.
    if (Map::Load(pszPath, &BaseActorFactory) == false) { Msg("Failed to load \"" + pszPath + "\" as path is invaild/missing from disk!"); return false; }  
    Custom Actors
    Now we have the platform to build our actor, lets do so! Here's sample code of a relay actor. I'm picking this class as it shows an input and output functions being used that'll work with the flowgraph as well as loading values.
    #ifndef LOGICACTORS_H #define LOGICACTORS_H #if defined( _WIN32 ) #pragma once #endif #include "stdafx.h" #include "baseactor.h" class LogicRelay : public BaseActor { long m_intDelayTime; long m_intTriggerTime; public: virtual void Start(); virtual void ReceiveSignal(const std::string& inputname, Entity* sender); virtual void UpdateWorld(); }; #endif #include "stdafx.h" #include "logicactors.h" //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void LogicRelay::Start() { BaseToggle::Start(); m_intDelayTime = GetIntValue("delay", 0); //FireOutput("OnStart"); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void LogicRelay::ReceiveSignal(const std::string& inputname, Entity* sender) { auto _event = String::Lower(inputname); if (_event == "trigger") { m_intTriggerTime = Timing::GetCurrent(); } } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void LogicRelay::UpdateWorld() { if (m_intTriggerTime > 0) { if (Timing::GetCurrent() > m_intTriggerTime + m_intDelayTime) { FireOutput("OnTrigger"); } } } Last we write the lua script:
    --[[ Purpose: A Logic entity that is used for relaying outputs. ]]-- Script.classname="logic_relay" Script.delay=-0--int "Delay" function Script:Trigger()--in end function Script:Outputs() self.component:CallOutputs("OnStart") self.component:CallOutputs("OnTrigger") end Compile your C++ code and attach your script to your entity. If all is well, you should have a working actor in your game! 
     
    Remarks
    This is the best and simple way of getting actors to work in your game much like the lua alternative. However, as a C++ user I can ensure you that your adventure isn't over. First you're going to have to figure out how actor members are deleted and when they should be deleted. The clear function in the world class will release all engine actors, but not assets so you need to take that into consideration. Also, I personally ran into issues with sound Source classes (Now called Speaker in Leadwerks 5) not being recreated when the map was reloaded. I ended up having to make my own source management system. To be honest, I forgot how it works since I haven't really touched my project in months. 
    Even after all my workarounds, I still have things not releasing properly sometimes, but if you're smarter than me, I'm sure you can figure it out.
     
    But I wrote this blog so if anyone had questions on how to use actors, You or I can point them here. Happy coding! 
  11. reepblue
    Through out the Leadwerks community you'll find two types of users. You'll find a large portion of the community using lua, and a small dosage of them using C++. This makes sense as people are more often going to purchase Leadwerks without any DLCs (including the Professional Edition), and get into developing. From experience, Leadwerks at the moment puts it's primary focus on Lua scripts with it's script editor and debugger, flowgraph editor, and the script property system. Everything is pretty straight forward and it's quick and easy to prototype and develop your games. Throw in an entity in the editor, attach a script to it, adjust some values, set up connections and you're done!
     
    You'll also find a smaller chunk of the user base talking about C++. Unfortunately.as of writing this blog post, a life of a C++ user is more difficult. For one thing, we don't have any connection with the editor so any entitys we want to put in our game need to be done during run time. If you made this cool monster in C++, you'd also need to make a system so your level designers (or you) can place them in the levels. Some examples of this is people using editor placed pivot's names as spawn markers. And even if then, you'd have to make a Lua script for that pivot to send any custom/modifiable values or special events to the C++ class. Not to mention, you'll need to search around how to communicate both languages together. And if people are suborn about learning Lua, they'll end up making systems that conflict with existing features/editor Things get messy fast when making gameplay with C++.
     
    Ok, so why do we bother then? While Lua is an ideal choice for gameplay, but what about the rest of your game? You still need to control how your window, context and world handles, how user settings get applied, and a lot other gross stuff that goes into a game. You can use Lua for that, but you'll run into two issues. For one, if you're working with a team, you'll have to trust them not to touch anything relating to the under the hood stuff unless they know what they are doing. You'll also have that paranoia of slow down due to using Interpreter for everything. You'll need a lot of script logic to cause any slow down, but who knows what you're doing when it comes to the background stuff.
     
    My solution is easy, use both together!
     
    My application Leadwerks Extended Executable is designed to handle all the yucky bits in the application side of things while you can use the engine as intended. All it does is instead of launching main.lua, it calls a App class which handles the window, context, and world along with reading user settings. As of version 2, you can communicate to the C++ classes in Lua with the AppManager Class
     

    -- This line will spam We're Connected" if we're in a map. if AppManager:IsConnected() then System:Print("We're Connected") end
     
    All the guts are "locked out" for the rest of the team so you don't need to worry about Mike from the art department accidentally touching the window handling system, and setting the project back a few weeks.
     
    LEX2 also has 2 classes to accommodate for C++ gameplay classes, Actors, and Puppeteers. Puppeteers act like a Lua script. You attach an entity to the class and write the code like if you were writting a Lua Script. We're still lacking an efficient way to connect an editor entity to a class. I tried to use a Lua script, but due to the fact you'd need a script per puppeteer for it's custom values and flowgraph support, you might as well just write the damn code in Lua and be done!
     

    #if !defined( PUPPETEER_H )
    #define PUPPETEER_H
    #pragma once
     
    #include "base.h"
     
    namespace Leadwerks
    {
    // Puppeteer's are pretty much the C++ version of lua scripts.
    // While using this class, keep note that this class doesn't exist in the game world.
    class Puppeteer : public Object
    {
    public:
    Puppeteer(Entity* pEntity);
    virtual ~Puppeteer() {};
     
    virtual void Start() {};
    virtual void PostStart() {};
     
    virtual void Collision(Entity* pEntity, float* position, float* normal, float speed) {};
    virtual void Draw(Camera* camera) {};
    virtual void DrawEach() {};
    virtual void PostRender(Context* context) {};
    virtual void UpdateMatrix() {};
    virtual void UpdatePhysics() {};
    virtual void UpdateWorld() {};
     
    void Detach();
    void RemoveAll();
     
    Entity* GetPuppet()
    {
    return entity;
    }
     
    Entity* entity; // The entity we'll be controlling. Was gonna rename this to 'puppet', but I want programing Puppeteer classes to be the same as scripting in lua.
     
    // We can get script values like this.
    bool GetBoolValue(std::string pValve)
    {
    return entity->GetBool(pValve);
    }
     
    float GetFloatValue(std::string pValve)
    {
    return entity->GetFloat(pValve);
    }
     
    int GetIntValue(std::string pValve)
    {
    return static_cast<int>(entity->GetFloat(pValve));
    }
     
    std::string GetStringValue(std::string pValve)
    {
    return entity->GetString(pValve);
    }
     
    protected:
    static void CollisionHook(Entity* entity0, Entity* entity1, float* position, float* normal, float speed);
    static void DrawHook(Entity* entity);
    static void DrawEachHook(Entity* entity);
    static void PostRenderHook(Entity* entity);
    static void UpdateMatrixHook(Entity* entity);
    static void UpdatePhysicsHook(Entity* entity);
    static void UpdateWorldHook(Entity* entity);
    };
    }
     
    #endif //PUPPETEER_H

     
     
    There are also Actors. Unlike how Puppeteers work in which you assign an entity to it, this time you're building on top of the object. and it exists in the world. It's only really good for players, but I ran into a pickle recently where the player would not spawn in the position I tell it to. I ended up translating the entire code to Lua and it works as intended. with the bonus of the flowgraph mind you.
     

     
    #if !defined( ACTORS_H )
    #define ACTORS_H
    #pragma once
     
    #include "base.h"
     
    namespace Leadwerks
    {
    // Actors are the entites, but behaving like it's under a lua script or Puppeteer.
    // Unlike scripts or Puppeteers, this class was ment to be the base of controlling the ACTUAL entity, without a middle class.
    class PivotActor : public Pivot
    {
    public:
    PivotActor(Entity* parent = NULL);
    virtual ~PivotActor();
     
    virtual void Collision(Entity* entity, float* position, float* normal, float speed) {};
    virtual void Draw(Camera* camera) {};
    virtual void DrawEach() {};
    virtual void PostRender(Context* context) {};
    virtual void UpdateMatrix() {};
    virtual void UpdatePhysics() {};
    virtual void UpdateWorld() {};
     
    protected:
    static void CollisionHook(Entity* entity0, Entity* entity1, float* position, float* normal, float speed);
    static void DrawHook(Entity* entity);
    static void DrawEachHook(Entity* entity);
    static void PostRenderHook(Entity* entity);
    static void UpdateMatrixHook(Entity* entity);
    static void UpdatePhysicsHook(Entity* entity);
    static void UpdateWorldHook(Entity* entity);
    };
     
    class ModelActor : public Model
    {
    public:
    ModelActor(std::string pPath, Entity* parent = NULL);
    virtual ~ModelActor();
     
    virtual void Collision(Entity* entity, float* position, float* normal, float speed) {};
    virtual void Draw(Camera* camera) {};
    virtual void DrawEach() {};
    virtual void PostRender(Context* context) {};
    virtual void UpdateMatrix() {};
    virtual void UpdatePhysics() {};
    virtual void UpdateWorld() {};
     
    protected:
    static void CollisionHook(Entity* entity0, Entity* entity1, float* position, float* normal, float speed);
    static void DrawHook(Entity* entity);
    static void DrawEachHook(Entity* entity);
    static void PostRenderHook(Entity* entity);
    static void UpdateMatrixHook(Entity* entity);
    static void UpdatePhysicsHook(Entity* entity);
    static void UpdateWorldHook(Entity* entity);
    };
     
    class CameraActor : public Camera
    {
    public:
    CameraActor(Entity* parent = NULL);
    virtual ~CameraActor();
     
    virtual void Collision(Entity* entity, float* position, float* normal, float speed) {};
    virtual void Draw(Camera* camera) {};
    virtual void DrawEach() {};
    virtual void PostRender(Context* context) {};
    virtual void UpdateMatrix() {};
    virtual void UpdatePhysics() {};
    virtual void UpdateWorld() {};
     
    protected:
    static void CollisionHook(Entity* entity0, Entity* entity1, float* position, float* normal, float speed);
    static void DrawHook(Entity* entity);
    static void DrawEachHook(Entity* entity);
    static void PostRenderHook(Entity* entity);
    static void UpdateMatrixHook(Entity* entity);
    static void UpdatePhysicsHook(Entity* entity);
    static void UpdateWorldHook(Entity* entity);
    };
     
    // TODO: Maybe make a SpriteActor?
    }
    #endif //ACTORS_H

     
     
    It was kind of hard writing this entry as I work with both languages. I guess the overall message is to chose you're battles and do what's in your skill set and preferences. I'd still love there to be an easy way to code gameplay elements in C++ like I do with Source. The class hierarchy system really comes in handy as you can just make a bunch of base classes and build off of them as needed. Hopefully, this will get addressed soon. In-fact, I actually made this blog post in reflection of this forum post. Maybe someday we can make our games fully in C++ and have access to the flowgraph editor and to be able to set custom properties!
  12. reepblue
    It's been over a year ago since I've released the Vectronic Demo using Leadwerks. The demo was composed of assets I've made ported from the Source Engine and my lack of understanding of the API and lua scripting at the time. (I'm used to coding everything in C++) As of writing, it has 1,221 subscribers on the Steam Workshop, 25 out of 26 positive ratings, and is the second most subscribed item! So there is sigh that this as a MVP (Minimum Viable Product) was a success. And again, the Game Launcher is in Beta..
     
    Since the demo, the engine got a lot of improvements and features, and I've learned a lot more about game development in general. (Models, Texture making, etc.) Last summer, I sat down to develop a base application for my projects. I wanted the world, context, and window management to be out of the way while making the full game, and I was concerned about a UI as it was a sticking point when I did a Source branch change from the 2013 SDK to the Alien Swarm Branch in favor of better development tools. (That engine is really fragmented when it comes to things like that....)
     
    Once I finished that application; LEX, I then started over developing the game using that template. Two months went by, I've done some interesting puzzles in dev textures.but I felt that one element was taking over the vecball power elements. It was more fun to use that than fire balls at boxes. (And it was more work than the Vecballs) So, I paused development on that project to research if that 'thing I added' would be a better focusing point.
     
    I only spent a week on it. I didn't want to work on it too much, just enough to see if it'll work. It turned out, that minus some placement calculation, it was really fun. As you can see, I don't want to talk about it in-case I decide to go with it.
     
    November was approaching again, and it's a special time for me. In 2010, I released my Portal mod Blue Portals with mostly positive reviews. Every November, I like to do something to give a reason for people to revisit it. Last year (2014) I fixed the mod so it'd work with the steampipe update, and last November, I decided to put the mod on Steam as an "Anniversary Edition"
     
    Although we had massive support about Blue Portals on Steam, there was things I wasn't happy about, and prevented me from putting it on Steam earlier. Some things didn't age well and I've made some poor design choices. There was also some criticism over the difficulty curve (which is kind of common with Portal mods), and somethings just broke. I can spend a few months patching it up, or I can take the extra time and reevaluate the entire project, and have it be a quality mod on Steam. Plus, if the difficulty curve was adjusted, more people can actually play it!
     
    This first started as a revamp project, but slowly became it's separate entity with it's own needs. I'm learning a few new things I've haven't known before, and it's interesting how some concepts/techniques I've learned using Leadwerks came into play here. I was first very worried about Vectronic being on the back burner, but then I realized that it's actually a good thing I take a break from Vectronic for the following reasons:
     

    I can experiment with marketing, community management, updates with little to no risk. I can try something and see how it goes and decide what I did right and/or wrong with that decision and use it in the future.
    I can get used to Steamworks, and have an idea going into Steam Greenlight how it's gonna be, which is a plus over most indie devs who need to take time to learn that stuff.
    Blue Portals: Anniversary Edition can be/will be used as an advertising piece for future projects, I can comfort future consumers with an example of our work before they vote on Greenlight or purchase it on Steam.
    I was really unsure where to take the project, so coming back with a clear head would help. I have a bunch of ideas now, but not gonna jump into anything now.

     
     
    I see the Blue Portals: Anniversary Edition project (BPAE) as a stepping stone for marketing and consumer confidence. Since the mod can be downloaded with zero risk to a consumer, I feel better taking chances with it then I would a product I'd wish to sell. So what's the battle plan?
     

    Continue to develop BPAE. I'm aiming for a December release. Since most research and development have been conducted five years ago, I think if my team and I keep working at it, we can have a polished product on Steam which in return can help sales with Vectronic. I plan on starting to experiment with Marketing next month. I really want to find that "How Early is the right time to show?" mark. My first guess is "When the project has an established direction, but nothing is 100% final just yet.". Which for this project is short of 6 months of development.
     
    Continue to chip away at LEX2 as it's going be the new foundation. Unlike it's predecessor, the solution is very minimal and allows for easy C++ entity implementations. I want to participate in the next Tournament using it to make sure it's all solid and good.
     
    When BPAE is wrapping up, start programming the yucky gross parts like the player and some custom collision rules. Start thinking how everything will tie together and use the new things I learned with BPAE while making design and development choices. I think doing this while BPAE becomes a touch up project will allow me to not feel like I'm doing the same things on two projects.
     
    Start the true cycle of development for Vectronic once BPAE is submitted to be released on Steam.

     
     
    So that's the plan. Taking this one at a time. I'll keep you posted on what I'm doing like always. Although Blue Portals: Anniversary Edition isn't a Leadwerks project, I still think me writing my findings with it can benefit others while making their games. We're all learning here!
  13. reepblue
    For the past few months, (on top of working on my greenlit mod) I've been developing a new foundation for Vectronic that will take care of all the 'internal yucky bits' such as window/context/world management, and other features and functions. I really wasn't happy how the first implementation came out as I felt like I was taking too much from the developer, and it was messy and confusing to do something simple like adding a loading screen.
     
    LEX2 is more lua friendly, not bloated with scripts that you may or may not use, and has more comments than my Blue Portals 2 video in the code! You just need the executables, App.lua, and engine.pak which contains standard assets that the application will call if something is missing.
     
    You can play with it here. I've used the latest beta with probes so if you don't have the probe beta, don't load map 07 I guess. Here are somethings you might notice looking at scripts or general usage.
     
     
    Developer Console:
     

     
    LEX2 comes with a developer console for visually debugging your game while you play. Draw stats, show physics, show navigation, or change settings on the fly. All commands use the standard system properties.
     

     
    To access the console, the game must be started with -dev or launched from the editor. When the game is paused, it the tilde key (`) and start typing. Hit Enter to submit your command. You can use PageUp and PageDn to scroll through pass commands.
     
    You can type 'help' to report a list of current commands. There is no display box, so it's best used when the game is windowed with the log from the script editor visible.
     
     
    Asset Manager:
     

    self.sound = Sound:Load(AssetManager:Check("Sound/MySound.wav))
     
    Never crash again because you moved something or mistyped an asset. AssetManager:Check() returns a error asset from engine.pak if what you've sent is not found. The game will not start at all unless the error assets are found at startup.
     
    You can also easily preload models for later use with AssetManager:Precache(string). You can also make a Precache list with AssetManager:PrecacheGlobal(string). This is great if you have a model or prefab that might spawn at anytime in any map.
     
     
    PostStart():
     
    PostStart is a command fired after the level loads instead of during it like Start does.
     

    function Script:Start() self.source = Source:Create() self.source:SetVolume(self.volume/100) self.source:SetPitch(self.pitch) self.source:SetLoopMode(self.loop) self.source:SetRange(self.range) self.source:SetPosition(self.entity:GetPosition(true)) end function Script:PostStart() local sound = Sound:Load(AssetManager:Check(self.soundfile)) if sound~=nil then self.source:SetSound(sound) if self.playing==true and self.enabled==true then self.source:Play() end sound:Release() sound=nil end end
     
     
    Input System:
     

    if Input:Forward(true) then self.input[1]=self.input[1]+1 end if Input:Backward(true) then self.input[1]=self.input[1]-1 end if Input:Right(true) then self.input[0]=self.input[0]+1 end if Input:Left(true) then self.input[0]=self.input[0]-1 end
     
    You can now allow your player to change their controls. by calling a general function. Controls can be controlled with System:SetProperty or by editing settings.config. The control commands are based off of the default steam controller layout. I just need too add previous/next weapon, but wasn't 100% sure how I was gonna do that since it may be a scroll wheel.
     
     
    Basic UI System:
     
    Use Lua to create your own UI with LEX2's exposed UI classes. If you replace the app start and loop functions with this code, it'll make a square in the corner with a text button that says "Leadwerks".
     

    --This function will be called once when the program starts function App:Start() Rect = UIRectangle:Create(4,4,200,200) Rect:SetColor(40, 40, 40) Rect:SetBorderLineWidth(4) Rect:SetBorderColor(92, 92, 92) Rect:EnableBorder(true) Rect:Hide() local testfont = Font:Load(AssetManager:Check("Fonts/consola.ttf"), 20) Rect3 = UIText:Create(Rect:GetCenter().x, Rect:GetCenter().y, "Leadwerks", testfont, "Test"); Rect3:Center(); Rect3:SetShadowMode(false) Rect3:SetAsInteractive(true) Rect:AddChild(Rect3); return true end --This is our main program loop and will be called continuously until the program ends function App:Loop() context:SetBlendMode(Blend.Alpha) -- Here is code to darken the scene when the game is paused. if self:IsPaused() and self:IsConnected() then context:SetColor(0,0,0,0.5) context:DrawRect(0,0,context:GetWidth(),context:GetHeight()) end Rect:Update() if (Rect3:GetMouseEvent() == Rect3.MouseLeftUp) then System:Print("Pressed!") elseif(Rect3:GetMouseEvent() == Rect3.MouseOver) then Rect3:SetColor(100, 100, 100) elseif (Rect3:GetMouseEvent() == Rect3.MouseLeftDown) then Rect3:SetColor(80, 80, 80) else Rect3:SetColor(255, 255, 255) end if window:KeyHit(Key.F11) then if (Rect:IsVisible()) then Rect:FadeOut() else Rect:FadeIn() end end context:SetBlendMode(Blend.Solid) return true end
     
    I haven't really did much with the UI as it's a future feature of the engine, plus I've been playing with other stuff. But with UIRectangle and UIText is pretty much the foundation of buttons, sliders and other stuff anyway. Or you can use another UI library if you want.
     
    Again, you can play with it by downloading the project here:
    https://dl.dropboxusercontent.com/u/16218991/Requests/LEX2Test.7z
     
    I'm really stress testing it now making sure I don't have to touch anything when I'm ready to revist Vectronic. Speaking of Vectronic, it's almost been a year since I've released the demo. I might later this week write a blog about it and my battle plan for it.
  14. reepblue
    Leaks of games happen, and they still happen. You can spend months and months working on your game, only for a server attack or the wrong person to share it with their buddies; which then goes public accidentally, (or intentional). Having an unintended work in progress build out there can give people the wrong idea about your game because there are people out there who think the development process is pretty straight forward.
     
    Let's take a look at the Half-Life 2 leak. The game, and the entire engine code was leaked and the entire thing was very unstable, It was also found out that all those
    were scripted, and broke when you actually play them. A lot of doubts and concerns about the game were made as they felt like they were lied to after seeing videos and screenshots not representing the leak at all. Of course, this later on generated a community of it's own, but that's a rare scenario. 
    Since Leadwerks is far more stable than a Source Engine build from 2003, you can have months of playable work leaked. If you indeed plan to sell your game on Steam or Desura, you most likely don't want you're game freely available. So how can we prevent this?
     
    If you already got a SteamAppID from Valve after a successful Greenlight campaign, there is nothing to worry as users need a key to access your game. But what about if all to your game is dev textures, rough code, and programmer art? If you only have the indie edition, or only use Lua scripts for your game, you can use the Game Launcher, and set your game to "Friends-Only". For games written in C++, or have a modified application, we can't do that as our program does not get uploaded to the workshop. With the Standard Edition of Leadwerks however, we can use more of the Steamworks API.
     
    A friend of mine was talking to me about how an old Source mod use to detect if an invalid Steam user tried to boot the mod, it would not boot. Once I heard this, I was eager to see if I could get a similar system to work.
     
    Every Steam user has a public unique ID that comes in many different forms. Steam and games use this to gather information about the user, among other things. In this case, we are going going to focus on the 64bit FriendID number. Thanks to the Leadwerks API, we can access Steamworks features without our game being on Steam!
     
    Let's us first start with creating a bool function under the main.cpp.
     

    bool CheckSteamID() {
    if (Steamworks::Initialize())
    {
    }
     
    System::Print("Error: Failed to initialize Steam.");
    return false;
    }
     
    So far so good! We make it a bool function so if we are not a fail to load Steamworks, or have the wrong ID, it will return false and close the program. Now let's get the users ID number with:
     

    uint64_t user = Steamworks::GetUserID();  
    As I said before, SteamID's come in various forms. The easiest to get is the 64 bit FriendID which appears in profile urls such as http://steamcommunity.com/profiles/76561197996502882/
     
    As you can see, my SteamID is 76561197996502882. For the rest of this article, I'm gonna use mine in place of where you should put yours. A way to get yours is to go on a site such as http://ihavenfi.net/steamid. Popping in the STEAM_0:0:******** format after putting your ID-less profile URL will give you your FriendID. Remember, these numbers are public, they aren't like credit card numbers, or anything.
     
    So let's continue! For this example, I'm going to use My SteamID. So let's make a simple if statement comparing the account trying to play this game with a hard coded value.
     

    bool CheckSteamID() {
    if (Steamworks::Initialize())
    {
    uint64_t user = Steamworks::GetUserID();
    if (user == 76561197996502882) // Me!
    {
    return true;
    }
    else
    {
    System::Print("Error: Invaild SteamID!");
    return false;
    }
     
    System::Print("Error: Failed to initialize Steam.");
    return false;
    }
     
    With this code, (and I hope you replaced the SteamID), your game will only work for you if you're logged into your account. But you need beta testers, and what about the other developers? Let's Continue by adding more IDs!
     
    As another example, let's grab Josh's SteamID. After all, he's the CEO and should be able to play any LE game he wants. Although his profile is private, we can still grab the ID-less URL which is: http://steamcommunity.com/id/Josh
     
    Pop it in the converter and we get: STEAM 0:0:6085996. Let's pop that value in again and we finally have his FriendID which is: 76561197972437720
     
    Now let's edit that if statement.
     

    if (user == 76561197996502882 // Me! || user == 76561197972437720) // Josh
    {
    return true;
    }
     
    Now, Josh can play our game if he's logged into his Steam account. The best way to get ID's is to make a Steam Group for your testers this way you have all the profiles listed that you need to code in. We're not done yet, we still need to call this function near the start of the main function.
     

    if (!CheckSteamID()) return 0;
     
    Just make sure you comment that out when your game does get to be on steam, or when it releases elsewhere with a different DRM measure if you desire.
     
    With this somewhat messy way, you can restrict your games to certain steam accounts without having your games on steam during development. As a bonus, let's welcome the user in the console to make the tester feel they're special. Put this before the function returns true.
     

    std::string username = Steamworks::GetUserName(user); System::Print("Hello there, " + username + "!");
     
    And that's it! There is most likely other ways of doing this, but this way is the simplest and easiest. If you hard code FriendIDs, and use the stand-alone publishing for beta builds, your game is just a bunch of unplayable bites and bits if it falls into the wrong hands!
  15. reepblue
    After a whole summer of working on the LEX template, and learning new things about the engine, I'm happy to say that Vectronic will soon be back on track in full development!
     
    Before I go into full detail, I wish to mention that the Vectronic Demo has been updated with Josh's optimization edits, and the VecBall has a new effect I've made during the development of LEX. If you had sluggish performance before, I recommend you check out the update!
     
    Now that I've got a nice foundation with a menu, a precache system, now what? I was thinking I would just be "upgrading" the Vectronic project to use the LEX template, but looking back on it, I'm not really happy how it is. Back in May, I really just ported textures, sounds, and models from my Source build to Leadwerks to see if a game like Vectronic would work on Leadwerks. Of course, I was still pretty new to lua scripting, and very use to pre-built lighting and such so I did struggle a bit, the demo map was not optimized, and most of my code is a mess. To wrap it up, I mostly did not know what I was doing. I still to this day see that demo map as a map from hell; I've had so many issues with it, but I'm happy how it turned out.
     
    Now 6 months later, I feel like I've learned a lot and ready to get back onto my main project again. I decided the best way to start is to start a new project based off of the LEX project, and redo most things. I've restarted this project many times over for the past few years, but this should be the last time.
     
    Back when I was developing this project on the Source Engine, I've felt like I needed assets to make a game. I thought if I had everything, textures, models, sounds and other assets done, I could build levels like a LEGO set; having all the pieces, and knowing where everything goes. It's a good theory, but it never really worked.
     




    A old Source build of Vectronic.


     
    I only had a few maps per build. The last build had 3 maps, and the build before that had 4 maps. There was never a set of interesting and fun levels, just a
    . Thing was, I was so worried about how things functioned and looked that no build ever got more then a few maps, I was distracted by other things that are not important in the pre-alpha stage of things, and so were my testers, which did not help move the project further ether.. 
    Learning from my mistakes, this time the project is going to start blank almost like the past few years never happened. Doing the reboot this way will force me to worry about scripts, and the actual puzzles. This will also make it easier to optimize the maps when it is time to bring in the assets, and not have what happened with the demo map. No lights, no models, just CSG and a few scripts. If the game is fun with just that, it's easy sailing from there!
     
    Now, that does not mean I'm gonna waste more time by re-scripting, re-modeling, and re-texturing things that are already done. There are somethings I think that can be better, and I do have ideas I wish to try, but not everything needs to be scrapped, somethings just need a revision. I eventually want to give Vectronic a look that only Leadwerks can give as the demo does look like it's running in Source. But right now, the basics need to be done!
     
    I hope to update every week on progress, I'm feeling this Friday I'll talk more about how I'm doing things and short term goals.
  16. reepblue
    Another week, another weekly report. Vectronic is going more smoothly then ever before taking things one at a time, and just shelling out maps and ideas quickly. Last week, I did a few improvements with how the player picks up objects, and fixing the issue of shooting projectiles through objects. I did not shell any more maps because I thought it would be better to focus on a small amount of puzzles at a time, perfecting them before making more maps. For this, I needed help.
     
    As of this week, I quietly put the first test build on in the Dropbox folder which used to host the Source build, and asked a small number of people to test out movement, mouse control, and if everything feels right before I make things more complex.
     
    From the first week of testing, I'm confident that the project is moving in the right direction. From one tester who has played all other builds of Vectronic told me that this current build has better puzzle direction and clarity, and full of gameplay unlike previous builds that was just a bunch of mechanics, pretty rooms, but no gameplay at all. I got recommended a few changes I should make, like making the box heavier so when it crashes to the floor after floating in the air, it does not bounce off a button, or adding a plane of glass to block a more frustrating solution. A few tweaks still need to be made, solutions should just work, and not fully relying on the physics engine. I recall this being a problem with Quadrum Conundrum.
     
    Word of advice though, I learned that play testers hate fully ambient lit rooms, so before you release for testing, atleast put fill lights in. One tester told me it was hard to perceive the room with a full ambient light. I have one map with fill lights as a test, and he reported that when he got to that map, he had a better experience.
     
    I also found out that this method on leak protection didn't work when you packaged your games. It failed to initialize Steamworks although Steam was running. Not sure if this has to do with how packaged games work, or something I did wrong. I could of just uploaded the raw project, but that's a difference of 88MB vs 18MB. I'll work on this for the next build which I plan on posting a new test build biweekly.
     
    VecBall Design
     
    Yesterday, instead of writing this blog post, I was in an art mood, and wanted to put something back online with a model and sounds. I was using the old ball sprite and particle effects from the Demo, and I was really unhappy on how the dispenser looked in Leadwerks compared to how it was in Source. I decided if I was gonna touch anything art related, it was gonna be the basic foundation of the Vecballs themselves.
     
    I already had an idea of how I wanted my balls to look in the previous build. I'm a huge fan of how the Portals where in Portal in pre-release builds, and I wanted to reference that.
     



     
    This portal design required a large amount of textures for the mask, overlay, and other things that portals need. These portals took a whopping 174MB of VRAM PER PORTAL. and this was 2006/2007. I'm told that Valve cut it only because they ran out of memory on the Xbox360. Besides them being memory hoggers, I'm happy that I have them in a mod where they work exactly like how they did in previous builds.
     
    Unlike Portals, I can do a sprial/swirl effect with my vecballs without going over budget. I really liked the idea of the balls being a swirling ball of energy, so this could work. I made a quick texture and effect before I focused more on LEX. I later put what I've did in the Vectronic Demo.
     
    I pretty much just copy and pasted my swirl edits I had before into this build with a few changes. It still needs work, but it's pretty much the same as it is in the Demo, at least for now.
     



     
    Now to the dispenser. What makes Vectronic diffrent from Portal is that you loose your vecballs after each level, requiring you to recollect them in the next map. When I first sat down for the design of the dispensers, I sorta saw them as the item pickup bases in multiplayer games like Unreal Tournament. In Source, the particles stood tall and really stood out. But when I was transferring the design to Leadwerks, no matter what, they appeared washed out. This might be because of the colors, but I could not replicate how they appeared in Source.
     
    So I decided to take the dispenser back to the drawing board. I felt that the bases were too small, and needed a face lift. I quickly made this out of a cylinder in blender, added some base colors, and called it a day. UV sheet is still bad though, something I always have problems with.
     



     
    Attached a few emitters and sprites to it and got this in-engine.
     



     
    This is still not final as the ball sprite can still get washed out in the environment. I'm not gonna worry about it until I start messing with post processing effects and the overall look of the maps. I'm also open to suggestions. It's good for now, and it's a hell of a lot better then how they are in the demo!
     
    So that wraps this weeks report up. I guess this weeks goal is to ask more people to test the seven maps I have and seeing how I can improve those and other things before moving forward.
  17. reepblue
    I almost let a whole week go by without writing a blog, ahh! I've been doing a lot of thinking and experimenting over the last few days, and talking to other people about Vectronic.
     
    Last week, I talked about improving functionalities of the pickup system and the platforms. Although the pickup system is near done, the platform system, well they need work. I made a topic about them here, and kind of left me on a stand still on the project because I see them as a vital piece of the game, and they need to be perfect 100% of the time.
     
    As I mentioned before Vectronic always seemed to feel and play like a demo. there was never more than 3 maps, and the ideas were very basic. The method of slopping out maps quickly, and fine tuning them later is fixing that as there are way more maps than ever, but unfortunately I'm noticing a problem with the actual core gameplay itself, something I would never notice with just a few demo maps.
     
    It seems as though although the box has cool effects attached to them, they mostly need environment elements to really make them shine; which is a problem. The question is, is it the powers attached to the balls, or is it that the balls can only activate the boxes. And if the balls can activate other elements, how would they work, and what problems would it solve? I went back to the project what Vectronic morphed out of to see if those ideas can fix problems that Vectronic has.
     




     
    In the old project, the power was tied to each box, and the player would fire electric bolts at them to activate or deactivate them. I had a cube that reversed it's gravity, (Along with other cubes because I didn't know how to make it so only THAT cube flipped) one that stayed static in the air, a cube that exploded, and a cube that did nothing at all. This method had problems, which I talked about here.
     
    The idea of the flip cube was something I really loved, but I couldn't figure out how to only make that one cube reverse gravity while the others remained normal. Out of curiosity, I decided to quickly script this cube and simply have it flip when the F key was pressed. I got the effect I wanted, but it's not flawless. what I did was when the cube was told to flip, it would use a joint to go to the ceiling. You can ride it up, but the player would clip through the ceiling which I think I can fix on the player side, and you can't pick up a box when the box wants to go up; but again I think I can fix that.I also have ideas on how to make the ghost cube more fun and interesting.
     
    But then comes the question is this really a problem, or is it just me working on an idea as a demo for such a long time that I find it boring? I'm asking a lot of people for input and one person plans on give me a playable prototype of his idea so I'm looking forward to that. I want Vectronic to be the best it can be, and if it ends up being different than the demo or what it ever was, but a lot more fun, so be it.
     
    Besides gameplay, I've also been playing with tolua++, and getting ideas what I wish to do with LEX in the future. There is also the Halloween Game Tournament so maybe my head will clear up when I'm working on that. There have also been some experimentation on art, but I'm not ready to talk about that yet.
  18. reepblue
    Loading sounds in Leadwerks has always been straight forward. A sound file is loaded from the disk, and with the Source class emits the sound in 3D space. The sound entity also has a play function, but it's only really good for UI sounds. There is also Entity::EmitSound() which will play the sound at the entity's location. (You can also throw in a Source, but it'll auto release the object when it's done.)
    While this is OK for small games, larger games in which sounds may change might mean you have to open your class, and adjust the sounds accordingly. What if you use the sound in multiple places and you're happy with the volume and pitch settings from an earlier implementation? You could just redefine the source in a different actor, but why should you?
    A solution I came up with comes from SoundScripts from the Source Engine. With that engine, you had to define each sound as a SoundScript entry. This allowed you to define a sound once, and it allowed for other sound settings such as multiple sounds per entry. I thought this over, and with JSON, we can easily create a similar system for Leadwerks 4 and the new engine.
    I first started with a dummy script so I can figure out how I wanted the end result to be.
    { "soundData": { "Error": { "file": "Sound/error.wav", "volume": 1.0, "pitch": 1.0, "range": 0.25 }, "RandomSound": { "files": { "file1": "Sound/Test/tone1.wav", "file2": "Sound/Test/tone2.wav", "file3": "Sound/Test/tone3.wav" }, "volume": 1.0, "pitch": 1.0, "range": 0.25 } } } In this script, we have two sound entries. We have an error sound (Which is suppose to be the fall back sound for an invalid sound entry) and we have a sound entry that holds multiple files. We want a simple, straight forward. entry like "Error" to work, while also supporting something "RandomSound" which can be used for something like footstep sounds.
    The script is streamed and stored into multiple structs in a std::map at the application start. We use the key for the name, and the value is the struct.
    typedef struct { std::string files[128]; char filecount; float volume; float pitch; float range; bool loopmode; } sounddata_t; std::map<std::string, sounddata_t> scriptedsounds; Also notice that we don't store any pointers, just information. To do the next bit, I decided to derive off of the engine's Source class and call it "Speaker". The Speaker class allows us to load sounds via the script entry, and support multiple sounds.
    You create one like this, and you have all the functionalities with the Source as before, but a few differences.
    // Speaker: auto speaker = CreateSpeaker("RandomSound"); When you use Play() with the speaker class and if the sound entry has a "files" table array, it'll pick a sound at random. You can also use PlayIndex() to play the sound entry in the array. I also added a SetSourceEntity() function which will create a pivot, parent to the target entity. From there, the Play function will always play from the pivot's position. This is a good alternative to Entity::EmitSound(), as you don't need to Copy/Instance the Source before calling the function as that function releases the Source as mentioned earlier. Just play the speaker, and you'll be fine! You can also change the sound entry at anytime by calling SetSoundEntry(const std::string pSoundEntryName); The creation of the Speaker class will start the JSON phrasing. If it has already been done, it will not do it again.
    Having sounds being loaded and stored like this opens up a lot of possibles. One thing I plan on implementing is a volume modifier which will adjust the volume based on the games volume setting.Right now, it uses the defined volume setting. It's also a part of another system I have in the works.
  19. reepblue
    Luawerks has been updated for Leadwerks 4.4, taking advantage of the new GUI system while adding new features and commands that allow you to develop your game better.
     
    Developer Console Refresh
    The Developer Console is the stable of the framework. Thanks to the GUI, you can now scroll through your history, highlight, and move the cursor around! Page through your history using the Page Up and Page Down keys! Having a console is vital for development as it allows developers to change maps and other values in-game instead of restarting the program after changing some code. You can add your own commands easily as well. For more information, see the page about the Console here.

     
    An Integrated Menu
    The standard release of Leadwerks 4.4 comes with a menu. I shipped my own standard menu with Luawerks to be more integrated with the rest of the framework. Unlike the official menu script, the Luawerks menu uses 2 GUI's; one for the main menu, and the other for the panels/windows. You can just edit the main menu without effecting the panels, and vise versa. Menu also calls the panels via console commands. 

     
    The Gotcha's Fixed
    Upon installing Luawerks into the project, I recommended that the user calls SetCamera() instead of Camera:Create(), so that the world class would know what camera to reference. This caused a problem when the user switched maps, so I also requested that the user releases the camera too. I replaced this with a for loop in the GetCamera() function, and calling camera:Release() isn't necessary.
    You can now load maps in-which their names have spaces! 
     
    Load Models as Copies
    I added a second parameter to the LoadModel() function. If set to true, the function will load a model, create and return a copy of the model, and release the original asset. This allows you to manipulate that model without effecting other references. This is great if you want one model to use multiple skins.
    This image shows two of the same models loading two different materials.

     
    Here is an example on how to create a copied/unique model:
    self.mymodel = LoadModel("Models/MyModel.mdl",true)  
    LOD Models [Experimental]
    An experimental feature I've added is an object with multiple models and it switches the models based on the camera's distance from the entity. This only works on Static objects as I didn't calculate velocity and mass.
    You'd need to make it an object script like so:
    function Script:Start() self.model = ModelLOD("Models/LodTest/main.mdl") self.model:Get():SetPosition(self.entity:GetPosition()) self.model:AddLOD("Models/LodTest/lod1.mdl",5) self.model:AddLOD("Models/LodTest/lod2.mdl",10) self.model:AddLOD("Models/LodTest/lod3.mdl",15) end function Script:UpdateWorld() self.model:Update() end As I said before, this was something I was playing with, and I don't think it's ready for full use yet. Although it'll help with poly counts, each ModelLOD needs to be updated to get the distance of the camera, so you'd need to weigh your costs.
    On top of all that, bug fixes were made, new commands were added, and more things are planned for future releases. Documentation will continue to be updated here.
     
    About Luawerks
    Luawerks is a Lua framework for video games developed on the Leadwerks Game Engine. It supplies developers with additional functions, handles the game loop and allows them to debug their code with a developers console. With all that out of the way, you can focus on making your game fun!
    You can purchase Luawerks from the Leadwerks Workshop Store for $9.99.
    For documentation and bug reporting, please visit the GitHub page.
  20. reepblue
    Hi,
     
    I've been reading and posting here and there on the forums for quite some time now, and being that my current project is taking shape, I think it's time that I introduce myself, and in the next post, I'll share some goals of my project.
     
    Quick Bio:
     
    As a Kid, I've had an interest in game development with my hundreds of hours I put into RPGMaker 2000/XP. At that time, I was mostly inspired by The Legend of Zelda series and other top down games that could be made in the program.
     
    When I was a young teen, I was not really into PC games. I stuck to my old Nintendo consoles while my brother would play Counter-Strike: Source on a daily basis. I remember him once showing me a custom map he made for CSS that be based off our local 7-Eleven store. He was showing me how he made the textures, and a bunch of other simple things. I can't recall if I was blown away, or wanted him to stop talking so I could do my thing to be honest.
     
    After a while, my interest in game development started to fade, and I got more into graphic design for websites and such. But then game called Portal. I was not into first person shooters, but I did like Puzzle games, and this game was one of a kind when it released. My brother got The Orange Box package and I played the living hell out of Portal. The game was so interesting to me and there came a point where I would turn on cheats, take the metal rectangles, move them into position, and save the game. Then a "custom puzzle" would be loaded like loading a saved state.
     
    And it begins!:
     
    To make a long story short, I eventually got The Orange Box for the PC, got a budget build, and made a lot of maps that never got released. Portal 1's gameplay elements where just a collection of base engine entities that worked together. I spent months on end learning how to make breakable glass, the indicator strips, buttons, doors, you name it! After I felt like I had enough knowledge of map making, and the I/O system, I decided to work on my first mappack called Blue Portals.
     
     



     
    The project was on and off. I was working on another project at the time, so these maps were totally remade from the ground up with a new art design.
     



     
    Through out the years and other side projects, Blue Portals was released on November 29th, 2010. The mod got mix reviews, and looking back on the project, I can say that it's not for everyone. But I can say that it does have a cult following.
     
    With the release of Portal 2, of course I was excited to work on custom maps. I first started to to work on a Blue Portals 2 to fix the flaws of the original, and to add other cool elements. But due to the different FileSystem structure of that branch of Source, and my lack of knowledge of it at the time, I canned it. It was a good thing too, because DLC1 made the unused Adhesion Gel weird which BP2 used.
     
    I've made some small mappacks such as A Little Higher, Tornate, and Feather Away. I also worked on most of the test chambers for Alive & Kicking while I was making new game concepts still within the Source Engine. After a while, Portal started to get boring, and the second DLC for Portal 2 (PTI) made me lose interest in making Portal puzzles because now anyone could do it, and it killed the art for me.
     
    I started a project in the Source Engine called Punt and to sum it up, it was me trying to find what I want as my own puzzle game.
     




     




     
    After a while, I got the gist that the main core concept of the Puntgun was not fun. That, and the code base was a mess due to the Puntgun being a mod of the gravitygun, and poor tracer code. Also, Valve released a new SDK base to replace the one from 2007, and I really wanted my mod to be less like Portal gameplay wise.
     
    In the next entry, I'll talk more about my project, the goals and why I decided to port it to LE3.
     
    In the meantime, you can checkout my Youtube channel to see what I've done throughout the years. (I hope to put Leadwerks related content on it soon!)
    https://www.youtube.com/user/reepblue
     
    And here is my blogger which as more of my writings and thoughts:
    http://reepblue.blogspot.com/
     
    Thank you for reading/watching. I hope you are willing to hear more!
  21. reepblue
    Very happy on how everything is coming along. Blue Portals: Anniversary Edition development is going a tad slower than I wanted it too, but it's coming along nicely. We've finally shipped that
    in late August, and the mod made even more progress since. We just got some unknowns to work out like voice acting and music. I got some puzzles in the pipeline, and the mod right now has 11 playable maps.  
    LEX2 is getting re-organized. I went back to the CMD window based dev console as I plan on adding in the final console with the new GUI once that's done and well documented. I also implemented a Convar system along with a new conommand system. Pretty much, a new foundation is in place, and I shifted my focus to be easier lua development for now. I'll write about it more when I know more on how I wish to accomplish this.
     
    Steam Dev Days is next week! I've spent the last month going through my archive of experiments and prototypes, along with making demo maps for Blue Portals: Anniversary Edition. Chris (my texture artist) is bringing his Surface to showcase our stuff. If you're gonna be attending the event, come find us! We've got a lot to show off and talk about. I'll most likely be posting on my twitter through out the event.
     
    Unfortunately, the surface ran any Leadwerks project terribly, so we are only limited to our Source projects.
     
    That's really it. I know I usually write an essay for blog posts, but I've been busy lately in preparation for Steam Dev Days. I'm really excited, as I never been on that side of the country before!
  22. reepblue
    After I've returned from Steam Dev Days, I caught a terrible cold, so my first week home was unproductive when it came to LEX or Blue Portals. But besides that, I had a great time! I mentioned briefly in my last blog post before I went that I was giving LEX some reorganizing and defragmenting. This blog post, I'm going to go more into detail.
     
    LEX2 has a lot of things going for it. The developer console is really handy, the AssetManager prevents crashes and has the ability to pre-loads things for you and the PostStart() command for entities allows you to make entities do things RIGHT AFTER the map loads instead of during the loading process. All very helpful!
     
    Crawler's Den taught me a lot about what I was doing with LEX2, and where I should go. I designed it to be more Lua friendly than the initial release. but also allowed easy C++ classes to be made. Crawler's Den had the challenge of "Make a whole game in Leadwerks using nothing but C++". I know it was do-able, but how hard could it be?
     
    Leadwerks today isn't really as optimized to be developed using C++ as it is to Lua. I had to do go through a lot of hoops with somethings that could have been done easily if I just wrote the game in Lua. For instance, making C++ classes required more thought, and to attach my C++ puppeteers (actors) to entities in the editor, I had to make a Lua script that acted like a Hammer fgd file to sync it's keyvalues, inputs and outputs. Oh and on top of that, I had to make sure that each puppeteer got deleted properly. All and all, it was do-able, just not ideal at all!
     
    I'm switching back to be more focused to making games with Lua for now. I started by making a new project and re-writing the console system to be more flexible. Instead of defining all ConCommands in one cpp file, ConCommands can be defined anywhere now like:
     

    static void ConCommand_SayHi(vector parameters)
    {
    System::Print("Hi")
    }
     
    static ConCommand sayhi("sayhi", ConCommand_SayHi);

     
    With this system, it may be possible to make your own console commands via lua, but I'd have to look into it.
     
    My ExSystem Class is derived from Leadwerks System Class, and it's how to talk to the application directly. It handles the dev console, pausing, resuming, and other states that the application might be in. 90% of ExSystem can be called from Lua.
     
    The Developer console is much more powerful than it ever was. You can now make entities fire script functions or outputs by the entity name, or using !picker when looking at it.
     

    fire <entityname> <functionname>
     
    There are also a lot of Utility functions from the previous codebase. One of my favorites is this function.
     

    inline Leadwerks::Entity* UTIL_PickFromCamera(int pDistance, int pCollisionType=0, float pRadius=0) {
    Leadwerks::PickInfo pickinfo = Leadwerks::PickInfo();
    Leadwerks::Vec3 p0 = UTIL_GetActiveCamera()->GetPosition(true);
    Leadwerks::Vec3 p1 = Leadwerks::Transform::Point(0, 0, pDistance, UTIL_GetActiveCamera(), NULL);
    if (UTIL_GetActiveCamera()->world->Pick(p0, p1, pickinfo, pRadius, false, pCollisionType))
    {
    return pickinfo.entity;
    }
     
    return nullptr;
    }
     
    I also made the settings.config file store in the AppData folder instead of the game's root. While I never had any permission issues with the old way, still best to have the user's settings there.
     
    I merged both the WindowManager and ContextManager to be one class; There is just a WindowManager now. There was no point to have the context be managed in it's own class; definitely now since I took out my UI code I had in there for the console drawing. I also fixed Screenshots being properly placed in it's own Directory in the game's folder!
     
    The World Manager is what got the most fat reduced. Before I had a great chunk of code that was ether commented or ifdef'd out. For example, I had old code for my cubemap factory before the Probes where a thing, and other checks that in the long run might cause more problems later. The World Manager is responsible that when a user want's to load a map, a world get's created or cleared, and PostStart is fired on all entities if they have a script. If the user want's to go back to the menu, the world manager clears, and then releases the world. Something that was 913 lines got reduced to 260, and it does pretty much the exact same thing.
     
    I removed the InputManager as at Dev Days I learned that that Steam will later be able to handle that for you, not exclusive to the Steam Controller. Also, the implementation was really confusing and tricky.
     
    Finally, I removed a bunch of classes that are no longer needed including the Puppeteer class as the focus is now Lua. I want to keep the scope of things small right now. All you need to convert a game using the standard executables to the LEX is to replace the 2 executables and add this self explanatory Lua script that'll override main.lua.
     

    --This function will be called once when the program starts function App:Start() return true end --This function will be called right after when the program starts. function App:PostStart() end --This is our main program loop and will be called continuously until the program ends function App:Loop() return true end --This function is called once when the program pauses time. function App:onpause() end --This function is called once when the program resumes time. function App:onresume() end --This function is called while the program is loading a map. (This is where you draw your loading screen!) function App:OnMapLoad() end --This function is called when the program finished loading a map. function App:OnPostMapLoad() end --This function is called when the program disconnected from a map. function App:OnDisconnect() end --This function is called when the program closes. function App:OnShutdown() end
     
    Since it's really those three files that need to be shipped for the product to work, I can use the the standard Leadwerks Project Manager again. I don't plan on releasing the source code at this time. Maybe when making C++ games becomes more streamlined I'll re-integrate my C++ systems.
     
    There are still somethings to do though:
    Integrate the dev console to the Leadwerks GUI when that's fully done. The CMD using Cin approach that I'm using now is kind of bad.
    Play with the app a few hours a day, fix bugs, build on it a little each week.
    Find a good method in distribution, and keeping people up to date.

     
    In the future, I also hope to add more classes you can use while making gameplay objects. Somethings like a Beam class, and such to make things much more simpler.
     
    I also hacked together this shader that takes in roughness and ao sheets along with the diffuse, normal and spec. My artist gave me some textures right from Mamoset and my goal was to import them to Leadwerks as close as I could. I think they came out really well.
     



  23. reepblue
    Premake is multiplication project maker.Unlike CMake, it simply generates a project file for the given IDE giving you a clean result. You only need the one light weight executable and a lua script for this to work.  I've spent today setting it up with Leadwerks. I haven't tested Linux yet, but it should work.
    My premake5.lua file:
    g_LeadwerksHeaderPath = "./Engine/Include" g_LeadwerksLibPath = "./Engine/Libs" function GlobalSettings() -- Include Directories includedirs { "%{prj.name}", "%{g_LeadwerksHeaderPath}", "%{g_LeadwerksHeaderPath}/Libraries/SDL2-2.0.10/include", "%{g_LeadwerksHeaderPath}/Libraries/NewtonDynamics/sdk/dgCore", "%{g_LeadwerksHeaderPath}/Libraries/NewtonDynamics/sdk/dgNewton", "%{g_LeadwerksHeaderPath}/Libraries/libvorbis/include", "%{g_LeadwerksHeaderPath}/Libraries/libogg/include", "%{g_LeadwerksHeaderPath}/Libraries/openssl/include", "%{g_LeadwerksHeaderPath}/Libraries/VHACD/src/VHACD_Lib/inc", "%{g_LeadwerksHeaderPath}/Libraries/glslang", "%{g_LeadwerksHeaderPath}/Libraries/freetype-2.4.7/include", "%{g_LeadwerksHeaderPath}/Libraries/OpenAL/include", "%{g_LeadwerksHeaderPath}/Libraries/NewtonDynamics/sdk/dMath", "%{g_LeadwerksHeaderPath}/Libraries/NewtonDynamics/sdk/dgTimeTracker", "%{g_LeadwerksHeaderPath}/Libraries/NewtonDynamics/sdk/dContainers", "%{g_LeadwerksHeaderPath}/Libraries/NewtonDynamics/sdk/dCustomJoints", "%{g_LeadwerksHeaderPath}/Libraries/RecastNavigation/RecastDemo/Include", "%{g_LeadwerksHeaderPath}/Libraries/RecastNavigation/DetourCrowd/Include", "%{g_LeadwerksHeaderPath}/Libraries/RecastNavigation/DetourTileCache/Include", "%{g_LeadwerksHeaderPath}/Libraries/RecastNavigation/DebugUtils/Include", "%{g_LeadwerksHeaderPath}/Libraries/RecastNavigation/Recast/Include", "%{g_LeadwerksHeaderPath}/Libraries/RecastNavigation/Detour/Include", "%{g_LeadwerksHeaderPath}/Libraries/tolua++-1.0.93/include", "%{g_LeadwerksHeaderPath}/Libraries/lua-5.1.4", "%{g_LeadwerksHeaderPath}/Libraries/glew-1.6.0/include/GL", "%{g_LeadwerksHeaderPath}/Libraries/glew-1.6.0/include", "%{g_LeadwerksHeaderPath}/Libraries/enet-1.3.1/include", "%{g_LeadwerksHeaderPath}/Libraries/zlib-1.2.5", "%{g_LeadwerksHeaderPath}/Libraries/freetype-2.4.3/include" } -- Global Defines: defines { "__STEAM__", "_CUSTOM_JOINTS_STATIC_LIB", "FT2_BUILD_LIBRARY", "LEADWERKS_3_1", "DG_DISABLE_ASSERT", "OPENGL", "_NEWTON_STATIC_LIB", "_STATICLIB" } -- Windows Exclusive: filter "system:windows" systemversion "latest" pchsource "%{prj.name}/stdafx.cpp" links { "libcryptoMT.lib", "libsslMT.lib", "Rpcrt4.lib", "crypt32.lib", "libcurl.lib", "msimg32.lib", "lua51.lib", "steam_api.lib", "ws2_32.lib", "Glu32.lib", "libovrd.lib", "OpenGL32.lib", "winmm.lib", "Psapi.lib", "OpenAL32.lib", "SDL2.lib", "Leadwerks.lib" } libdirs { "%{g_LeadwerksLibPath}/Windows/x86", "%{g_LeadwerksLibPath}/Windows/x86/%{cfg.buildcfg}" } defines { "PSAPI_VERSION=1", "PTW32_STATIC_LIB", "PTW32_BUILD", "_NEWTON_USE_LIB", "_LIB", "DG_USE_NORMAL_PRIORITY_THREAD", "GLEW_STATIC", "WINDOWS", "WIN32", "OS_WINDOWS", "PLATFORM_WINDOWS", "_WIN_32_VER" } buildoptions { "/D \"SLB_LIBRARY\"", } flags { "NoMinimalRebuild" } linkoptions { "/NODEFAULTLIB:MSVCRT.lib", "/NODEFAULTLIB:MSVCRTD.lib" } -- Linux Exclusive: filter "system:linux" systemversion "latest" linkoptions { "-ldl", "-lopenal", "-lGL", "-lGLU", "-lX11", "-lXext", "-lXrender", "-lXft", "-lpthread", "-lcurl", --"-lSDL2", "%{g_LeadwerksLibPath}/Linux/libluajit.a", "%{gameDir}/libopenvr_api.so" } defines { "ZLIB", "PLATFORM_LINUX", "unix", "_POSIX_VER", "_POSIX_VER_64", "DG_THREAD_EMULATION", "DG_USE_THREAD_EMULATION", "GL_GLEXT_PROTOTYPES", "LUA_USE_LINUX", "_GLIBCXX_USE_CXX11_ABI", "_CUSTOM_JOINTS_STATIC_LIB" } linkoptions { "%{g_LeadwerksLibPath}/Linux/%{cfg.buildcfg}/Leadwerks.a" } -- Debug Build: filter "configurations:Debug" runtime "Debug" symbols "on" targetsuffix ".debug" defines { "DEBUG", "_DEBUG" } if os.target() == "windows" then links { "newton_d.lib", "dContainers_d.lib", "dCustomJoints_d.lib" } end -- Release Build: filter "configurations:Release" runtime "Release" optimize "on" if os.target() == "windows" then buildoptions { "/MP" } links { "newton.lib", "dContainers.lib", "dCustomJoints.lib" } end end function GenerateLuaApp() workspace "PremakeTest" architecture "x86" --architecture "x86_64" startproject "LuaApp" configurations { "Debug", "Release" } -- Test application project "LuaApp" kind "ConsoleApp" language "C++" location "%{prj.name}" staticruntime "on" -- Project Directory: projectDir = "%{prj.name}/" -- Game Directory: gameDir = _WORKING_DIR .. "/../Projects/%{prj.name}" -- OBJ Directory objdir (projectDir .. "%{cfg.buildcfg}_%{prj.name}") targetdir (gameDir) files { "%{prj.name}/**.h", "%{prj.name}/**.cpp" } pchheader "stdafx.h" -- Custom Defines defines { "__TEST_ME_", } GlobalSettings() end newaction { trigger = "luaapp", description = "Builds the stock lua app", execute = GenerateLuaApp() } if _ACTION == "luaapp" then GenerateLuaApp() end Then I just have batch file that builds the project file.
    "devtools/premake5.exe" vs2017 luaapp pause My impressions are more positive on this than CMake as CMake creates a lot of extra files to do real time updating if the CMakeList file is updated. Premake simply just builds the project file and that's it. It really reminds me of VPC stuff I had to deal with in my modding days. Really interested to how codelite projects generate on Linux.
  24. reepblue

    Cyclone
    As a teenager, I've spent thousands of hours with the Source SDK. It was really cool how Valve gave people the real tools to develop and create maps and mods. Overtime I got to see and take apart games to see how they actually work.
    Overtime, the indie engine market took over, and making Source mods is now just a terrible experience. Hammer crashes more frequently and the SDK branch of the engine has old bugs that were never patched. I want the excitement I had of making maps and cool features to be part of Cyclone as a package. Leadwerks has a very intuitive interface, and with a Lua Sandbox, many possibilities open up.
    As of the last update shipped today, anyone with a license of Leadwerks Game Engine has the ability to create custom maps and scripts and share them with others. This is the first step to see if anyone would be interested if they had the tools in their holster.
    There's now a batch file called "create_mod_env.bat" which will create a Leadwerks project within the game directory by default. You can edit the batch file to define a different path if you wish. You'll also receive a few example maps showing the logic of the game. Import the project in Leadwerks, and you should be good to go! When you're ready to share, zip up the map and chapter script and tell people to install it in a folder called "Addons" within the game's directory.
    While this is only the first draft of this, I'm interested to see where it will end up. I hope you all find it interesting and motivating.
  25. reepblue
    With the new engine coming along and me noticing limits with Cyclone's current architecture, it's time for me to start thinking about the mistakes I've made and how would avoid repeating them with the new engine. Luckly, Ultra Engine is far more flexible than Leadwerks thanks to smart pointers and the event system. I wish to share some of my plans going forward.
    Log, Log, Log Everything!
    I personally never look at my log files. I instead look at the CMD output and the debugger to see what's going on. However, end users don't have that luxury. The engine will automatically log its warnings and error messages, but it'll not tell you when that message was printed or the events that led up to it. Creating your own log stream like this will give you far more control over your log file.
    // Prepare the log file. auto logfile = WriteFile("Game.log"); // Main loop bool running = true; while (running) { while (PeekEvent()) { const auto e = WaitEvent(); if (e.id == EVENT_PRINT) logfile->WriteLine(e.text.ToString()); } } logfile->Close(); logfile = NULL; You can easily do add time stamp before the message, so you have an idea when the event actually happened. You can use real world clock time or the current application time.
    Do Nothing OS Specific (Unless It's Aesthetic)
    Cyclone is a Win32 application. Period. It works wonderfully on Proton and there isn't any reason for me to install an outdated version of Ubuntu and waste time making a native build for Linux that'll only sometimes work. I took the liberty of using the Windows API to create my own Input System, XInput integration, and the Splash Window. I also created a custom DLL for my Input System so the Action Mapper application would use the same system as the game. 
    Going forward, I want my code to work wherever Ultra Engine can run on. This means sticking to the API for mostly everything unless I need to use a 3rd party library (Like Steamworks). Still, 98% of your users will be using Windows so you might as well add some nice touches like the application knowing what theme the user is using and dressing your application accordingly like this.
    Launch Arguments != Settings
    If you've programmed with Leadwerks, you should be familiar with System::SetProperty() and System::GetProperty(). This by default will register a launch argument as a setting and then save it to a settings file. Cyclone works like this too, but arguments don't get saved to the settings file to avoid confusion. 
    I never used arguments to override settings. I only used -devmode, -console, -fullscreen, +screenwidth and +screenheight. So, instead of mixing launch arguments with user settings, I think just going to keep them separated and only use the arguments to tell the application how to launch. 
    Everything Will Be One
    Cyclone ships with 2 applications. One being the main game, and the other being the Action Mapper for key binding. They both share a dynamic library. This is because Cyclone is made using Leadwerks and the Action Mapper is made using Ultra App Kit. I want to make a Console window to remove it from the UI and a dedicated window for graphic settings would be handy. My new approach will make these separate windows that can be called with a launch command. 
    Compartmentalize Everything
    I ran into many conflicts when adding working on the Flag Run update. Cyclone was structured to be a Puzzle game, and here I was adding clocks and an auto reload system. I had to do a lot of edits to my code to make it work and not break the older maps. 
    Because of how Leadwerks worked, I had to do a lot of mixing and cross referencing of classes. In the end, it's all very messy now and it discourages me from adding to it more. My game application code will only create the window, framebuffer and camera. It's also will be responsible handle scenes and emit events. The components should do everything else. I plan to make a component for the always existing camera entity and have it listen to events when to show UI elements or change its settings. I can make the UI and settings components separate too! 
    Since there will be multiple "apps" in one application, I kind of have to do this. Thankfully, I don't have to do things like create my own reflection system this time...
    Get The Essentials Right, The First Time
    When you're making your first game, you generally want it up and running as soon as possible. Everything else critical you'll say "Meh, I'll worry about that later". Why would you spend time on things like an Input System or A Sound System if you don't even know the game is fun? A month or so later, you find yourself figuring out how the new systems you just built should be integrated with the existing game code. It's not funny, it's not fun. 
    I already have a game, and I know what works so I don't need to repeat that again. And being that the engine isn't 100% yet, I have plenty of time to get the essentials right. Here is what I'll be spending the first few months on.
    Ensuring that windows display and activate correctly. A splash window should show up, and the next window should be brought forward 100% of the time. Cyclone has a bug right now which a game window doesn't get brought to the top although I'm using every API call to make it do so. Settings system should be in-place before drawing a 3D cube to a framebuffer.  I have a very good input system in Cyclone already, but it relies heavily on WinAPI to work. I also include XInput code which gets overwritten by Steam Input anyway. The input system will now need to use the engine's API instead of Windows.  This system will work exactly the same as I have it today, but I think I'm going to leave out controller integration for now. I really liked using FMOD, but to lower dependencies, I think I should try the new OpenAL-Soft sound engine. I plan for my new system to read json scripts for sound entries that contain data for the Speaker and close captioning to display on the screen.    I plan to get cracking within a few weeks! I set up my PC with the RTX 3600 running Windows 10 to develop and stream to Discord! I noticed a lot of slow down with my GTX 1050 running the engine in fullscreen so it's time to use something with more power. I am keeping an eye out for a good price on a 4070 Ti though.  
×
×
  • Create New...