Jump to content


  • Content Count

  • Joined

  • Last visited

Community Reputation

501 Excellent


About reepblue

  • Rank
    Advanced Member

Profile Information

  • Gender
    Not Telling

Recent Profile Visitors

9,500 profile views
  1. reepblue

    How to use the underwater shader?

    I think a while back I just used the wobble.shader in the stock assets. The trick is checking if the camera is below the world's water level and applying/removing the effect respectively. You can also use the engine fog system with the wobble shader and get a decent result.
  2. Really interesting, Although I have a current day job, I'm always looking for new opportunities. Gonna hold on a PM for right now but you know my work I've done with Leadwerks and my YouTube channel has the rest of my history.
  3. With a little help from a virtual machine, I got my app to compile on Linux. The app is also cross compilable with Turbo too (On Windows, ofc).

  4. reepblue

    Load Package not keeping folder hierarchy

    To be honest, the end user should decide how they want their games to be broken up. This way a hot fix doesn't mean the entire game to be redownloaded if the developer made a pack for scripts and another for materials and models and so on.
  5. reepblue

    More Control On Controllers

    The Leadwerks API has standard Boolean functions that detect when the end user has pressed a key. While this is very simple and easy to understand, the issue comes when you wish to support binding of actions. Instead calling functions when a certain key was pressed or held, a better way to detect key events is to assign a key to an action. (e.g: Is the Jump key pressed). In Luawerks, I've written an action script in which returns the window call if the key defined in the user's settings is hit. The downsides is that you had to do this for every action in your game and that this was a system written for Luawerks so it wasn't very portable for non-lua/Leadwerks projects. function Action:MoveForward() if self.suspend==true then return false end local key_forward = GetKeyInt(System:GetProperty("key_forward","w")) return window:KeyDown(key_forward) end For those who don't know, SDL is an Open Source library that comes in handy for things like Window management, input, and such. So over the weekend, I've decided to sit down and create my own Input System in which would be allow for action bindings, portable for any type of project while supporting Game Pads outside of Steam. The first step was to manually poll all inputs and treat them all the same. For the mouse and keyboard, this was just seeing if a key was hit, and registering that event to a map. namespace RInput_KM { void SimulateButton(const Sint32& pKey, bool bDown); const float ButtonDown(const Sint32& pKey); void ShowMouse(); void HideMouse(); void SetMousePosition(const int x, const int y, bool bGlobal = false); void ModMousePosition(const int x, const int y, bool bGlobal = false); void UpdateMousePosition(); void SimulateMouse(const int x, const int y); const int GetMouseX(); const int GetMouseY(); void SetMouseWheelPosition(const int y); const bool OnMouseWheelUp(); const bool OnMouseWheelDown(); // Returns the long to a character. const char* GetButtonName(const Sint32& pButton); // Returns the character to a long const Sint32 GetButtonIndex(const char* pButton); void Enable(); void Disable(); void FlushKeyboard(); void FlushMouse(); void Flush(); } When it comes to buttons, it doesn't matter if the mouse button was pressed or a key stroke. What matters is that a button on the keyboard and mouse combo was pressed. I treat the keyboard and mouse as one controller. You can also "Turn off" the mouse and keyboard if you want. namespace RInput_GamePad { typedef enum { ENUM_GAMEPAD_NULL = -1, ENUM_GAMEPAD_ONE, ENUM_GAMEPAD_TWO, ENUM_GAMEPAD_THREE, ENUM_GAMEPAD_FOUR, ENUM_GAMEPAD_MAX = ENUM_GAMEPAD_FOUR } GamePadIndex; typedef struct { SDL_GameController* controller; const char* pszDeviceName; bool bEnabled; std::map<Uint8, bool> mControllerButtons; } gamepad_t; void Connect(const Sint32& pWhich); void Disconnect(const Sint32& pWhich); gamepad_t GetDeviceFromPort(const GamePadIndex& pPort); // Digital input: void SimulateButton(const Sint32& pWhich, const Uint8& pButton, bool bDown); const float ButtonDown(const Uint8& pButton, const GamePadIndex& iIndex = ENUM_GAMEPAD_ONE); const char* GetButtonName(const Uint8& pButton); const Uint8 GetButtonIndex(const char* pButton); // Analog input: void UpdateAxisMotions(const Sint32& pWhich, const Uint32& pAxis); const Sint16 GetAxisValue(const Sint32& pWhich, const Uint32& pAxis, bool bFlip = false); const float GetAxisFloat(const Sint32& pWhich, const Uint32& pAxis, bool bFlip = false); void Flush(const Sint32& pWhich); void FlushAll(); } Next was the game pads. which were a bit more challenging as you need to consider multiple game pads and the valves of analog inputs. If you were paying attention, You would notice that the ButtonDown functions for both the keyboard+mouse and the game pad are returning floats. While it may be limiting for certain applications, I've created "fake buttons" for events for when the trigger is pressed, or if the left stick is to the left. All input returns a float ranging from 0.0 to 1.0. For digital inputs like buttons, this will be a 1 or 0, but for analog the value depending the range from the resting point to the max will called. So if the left stick is all the way to the left, you'd get 1; half way 0.5. This is much better then dealing with direct Uint32 values. Last was to merge both controllers into one entry point in which we use in our app to register our actions, and check to see if they are being called. namespace RInput { typedef enum { CONTROLLER_KEYBOARDMOUSE, CONTROLLER_GAMEPAD } Controllers_t; void Init(SDL_Window* pWindow); void InitSDL(const void *data); void SetActiveDevice(const Controllers_t& pDevice); Controllers_t GetActiveDevice(); const char* GetActiveDeviceAsString(); const char* GetGamePadDeviceAsString(const int pPort); const Sint8 GetGamePadCount(); void TestEvents(const SDL_Event& pEvent); void PollEvents(); // <- Use this function if you're not using SDL event polling. void Flush(const Controllers_t& pController); void FlushAll(); //==================================================================== typedef struct { Sint32 key; Uint8 button; bool bDown; bool bHit; } action_t; const float GetActionInput(action_t& pButton); void RegisterAction(const std::string& pActionName, Sint32 iKey, Uint8 iButton, bool bConistant); void ModifyAction(const std::string& pActionName, Sint32 iKey, Uint8 iButton); action_t& GetAction(const std::string& pActionName); bool LoadActionsFromFile(const char* pszPath); void UpdateGamePadStickAsMouse(const Sint32& pWhich, const Sint8& pAxis); } Actions can be bind to a with key and a button. bDown is used to check if the action is being held down while bHit is a flag to check if this is something that should be called once per pressed (Like a key to pick up a box or something.) One top of all this, Actions can have their key/button bindings changed via an XML file. Here's an example how to use this with Leadwerks. #include "Leadwerks.h" using namespace Leadwerks; #include "rinput/rinput.h" int main() { Leadwerks::Window* window = Window::Create(); RInput::InitSDL(window->hwnd); Leadwerks::Context* context = Context::Create(window); World* world = World::Create(); Camera* camera = Camera::Create(); camera->SetRotation(35, 0, 0); camera->Move(0, 0, -6); Light* light = DirectionalLight::Create(); light->SetRotation(35, 35, 0); light->SetShadowMode(0); Model* model = Model::Box(); model->SetColor(1.0, 0.0, 0.0); model->SetPosition(0, 0, 0); model->SetShadowMode(0); RInput::RegisterAction("moveforward", KEYBOARD_W, GAMEPAD_BUTTON_LSTICK_UP, true); RInput::RegisterAction("movebackward", KEYBOARD_S, GAMEPAD_BUTTON_LSTICK_DOWN, true); RInput::RegisterAction("moveleft", KEYBOARD_A, GAMEPAD_BUTTON_LSTICK_LEFT, true); RInput::RegisterAction("moveright", KEYBOARD_D, GAMEPAD_BUTTON_LSTICK_RIGHT, true); RInput::RegisterAction("jump", KEYBOARD_SPACE, GAMEPAD_BUTTON_A, false); RInput::LoadActionsFromFile("controller.xml"); while (window->Closed() == false) { RInput::PollEvents(); float x = 0; float z = 0; float u = RInput::GetActionInput(RInput::GetAction("moveforward")); float d = RInput::GetActionInput(RInput::GetAction("movebackward")); float l = RInput::GetActionInput(RInput::GetAction("moveleft")); float r = RInput::GetActionInput(RInput::GetAction("moveright")); if (u != 0) { z += 0.05 + (u /10); } if (d != 0) { z -= 0.05 + (d / 10); } if (l != 0) { x -= 0.05 + (l / 10); } if (r != 0) { x += 0.05 + (r / 10); } model->Move(x, 0, z); x = 0; z = 0; Leadwerks::Time::Update(); world->Update(); world->Render(); context->DrawStats(0, 0, false); context->Sync(false); } return 0; } The Result: There are many things I didn't add for the sake of time or I wasn't sure how to implement like supporting action for multiple controllers on the front end. But this does what I was set out to do, and although I haven't tried, I'm confident that with little to no work, this code will work on macOS and Linux. As long as there is SDL2 on the platform, this will work. Now no matter if the project is Leadwerks, Turbo or just an SDL2 project, I now have a portable input system with controller support which you can find here.
  6. reepblue

    [4.6 Beta] Button always display as links.

    This was in a new 4.6 project. The build at the time had different GUI scripts. Reverting the scripts to the 4.5 versions fixed the issue.
  7. reepblue

    Luawerks Updated

    Luawerks has been updated to 1.2.7, making some small adjustments and fixes to the system. If you have previously purchased Luawerks, this update is available for free on the Leadwerks Marketplace. Please note: Luawerks is not ready for Leadwerks 4.6 and it's recommended to use Luawerks with Leadwerks 4.5. Following changes include: Added an optional Map selection menu. "New Game" will default call a panel in which all the maps in your Maps directory will be listed, Note: This will only work if the maps are not included in your zip as raw Lua functions are called. This will also not work if the Lua sandbox is enabled. Re-located error.mdl to a sub directory "Models/Developer" Added widget.mdl and axis.mdl for assistance for visualizing matrix results. When in-game, "Exit to Menu" will show instead of "Quit." This will release the world and the player will be returned to the main menu. Arrange locations of various functions. The names remain the same. Added various functions needed for this update. 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 Marketplace for $9.99. For documentation and bug reporting, please visit the GitHub page.
  8. I've updated Luawerks just now on the Marketplace and the Steam Workshop. I'll be writing a blog about this update tonight.


  9. reepblue

    Steam Achievements

    From my understanding, no. You need to call the use the Steamworks API with C++. You can always expose these classes yourself if you have the professional version.
  10. Luawerks update coming soon.

    1. Show previous comments  1 more
    2. reepblue
    3. mdgunn


      I'll check when it drops but maybe for the benefit of others what are the changes?.

    4. reepblue


      Right now there will be an optional map picker in the menu. Instead of New game just loading a map, a new panel will be called showing all the maps in the directory.

      It's copy/paste code from the VecTec project but I feel it's a needed feature.

      Experimenting with other stuff and this has to work with 4.6 out of the box which right now it doesn't as you need still 4.5's GUI scripts. Submitted a bug report.

  11. reepblue

    [4.6 Beta] Crash when using Joint:Ball()

    Yep, the code on that page just creates a Window and crashes.
  12. I've noticed that with the pickup system in the template FPS player is now very sloppy. With my code, it's much better with my system, but I've noticed a slow delay between updating the rotation of a clamped object between 4.5 and the 4.6 beta. Here is my Pickup Controller code if need be. It worked perfectly in 4.5.The SDK system is all rubberbandy and wack. if PickupController~=nil then return end PickupController={} function PickupController:Create(entity) if entity== nil then Debug:Error("PickupController: Entity cannot be nil!") end local pickupcontroller = {} pickupcontroller.entity = entity pickupcontroller.allowpickup = true pickupcontroller.maxdist = 2.5 pickupcontroller.carrydist = 1.75 pickupcontroller.carriedobj=nil pickupcontroller.effector=nil pickupcontroller.carrypos=Vec3(0) pickupcontroller.carryquat=Quat(0) for k,v in pairs(PickupController) do pickupcontroller[k] = v end return pickupcontroller end function PickupController:Reset() if self.effector ~= nil then self.effector:Release() self.effector = nil end if self.carriedobj ~= nil then local n = self.carriedobj:GetKeyValue("collisiontype") n = n tonumber(f) if n ~= nil then self.carriedobj:SetCollisionType(n) end self.carriedobj:SetKeyValue("collisiontype", "") self.carriedobj = nil end end function PickupController:Delete() self:Reset() --self.entity = nil self.effector=nil self.carrypos=nil self.carryquat=nil self.maxdist=nil self = nil end function PickupController:CanPickupObject(entity, masslimit) if not self.allowpickup then return false end if entity == nil then return false end if entity:GetMass() <= 0 then return false end if entity:GetShape() == nil then return false end if entity:GetAABB(Entity.LocalAABB).size.x > 1.28 or entity:GetAABB(Entity.LocalAABB).size.z > 1.28 or entity:GetAABB(Entity.LocalAABB).size.z > 1.28 then return false end if entity:GetAABB(Entity.GlobalAABB).size.x > self.carrydist or entity:GetAABB(Entity.GlobalAABB).size.z > self.carrydist then return false end if masslimit > 0 and entity:GetMass() > masslimit then return false end if entity:GetCollisionType() == Collision.Character or entity:GetCollisionType() == Collision.Scene then return false end return self:NotOnTopOfObject(entity) end -- Check to see if we're not on top of an object function PickupController:NotOnTopOfObject(object) if object ~= nil then local pickInfo = PickInfo() local p0 = self.entity:GetPosition() local p1 = Transform:Point(0,-1.0,0,self.entity,nil) if self.entity.world:Pick(p0, p1, pickInfo, 0.8, true, Collision.Debris ) then if pickInfo.entity == object then return false end end end return true end function PickupController:IsHoldingObject() if self.carriedobj == nil then return false end return true end function PickupController:PickupObject(entity, transformfrom) -- If we are already holding an object, don't call this function if self:IsHoldingObject() then return end self:Reset() self.carriedobj = entity -- Store it's original collision mode within the entity itself. self.carriedobj:SetKeyValue("collisiontype", tostring(self.carriedobj:GetCollisionType())) -- Change the object's collision type to "Debris" This has it's own issues, but it's less cost effective than raytraces. self.carriedobj:SetCollisionType(Collision.Debris) -- Play a sound on pickup.. local entscript = GetEntityScript(self.carriedobj) if entscript~=nil then if type(entscript.PlayImpactSound)=="function" then entscript:PlayImpactSound() end end self.carrypos = Transform:Point(0, 0, self.carrydist, transformfrom, nil) self.effector = Joint:Kinematic(self.carrypos.x, self.carrypos.y, self.carrypos.z, self.carriedobj) self.effector:SetFriction(1000, 1000) self.effector:SetTargetAngle(1) self.carriedobj:SetDamping(1, 1) self.carryquat = Transform:Rotation(self.carriedobj:GetQuaternion(true), nil, transformfrom) end function PickupController:UpdateHeldObject(transformfrom) if not self:IsHoldingObject() then return end -- Update the pickup position self.carrypos = Transform:Point(0, 0, self.carrydist, transformfrom, nil) local current_obj_pos = self.carriedobj:GetPosition(true) local diff = self.carrypos:DistanceToPoint(current_obj_pos) if diff > self.maxdist then self:DropObject() return end self.effector:SetTargetPosition(self.carrypos, 1) self.carriedobj:SetVelocity(self.carriedobj:GetVelocity() / 1.5) self.effector:SetTargetRotation(Transform:Rotation(self.carryquat, transformfrom, nil), 1) end function PickupController:DropObject() if not self:IsHoldingObject() then return end -- After our AABB test, if we can drop the object, do so local aabb = self.carriedobj:GetAABB(Entity.GlobalAABB) local player = self.entity:GetAABB(Entity.GlobalAABB) if not aabb:IntersectsAABB(player,0) then self.carriedobj:SetVelocity(self.carriedobj:GetVelocity() / 4) self.carriedobj:SetDamping(0.1, 0.1) self:Reset() end end function PickupController:Enable(b) self.allowpickup = b end
  13. I was reviewing Luawerks for the 4.6 update and found that the GUI scripts have been updated.I was first greeted with an error. Figuring out what changed made me start the standard menu script and I was greeted with the buttons always being links. Here is Luawerks in 4.6 using the older GUI scripts. Also note that check boxes don't seem to show correctly ether. This seems to be due to you've made in Buttons.lua. Was this edit necessary? If there is going to be no fix for this, I'm just going to ship the older scripts with my package.
  14. reepblue

    Connecting to client over network. (4.6)

    I personally just want a client/server soultion in which the application hosts a server localy and then creates a client to connect to the server; kind of what Source does. I have to experiment to see if it's practical. I still only wish to make Single player games, but I don't want to be limited. Having a system like described above will make any game multiplayer with a flip of a switch.
  15. reepblue

    Connecting to client over network. (4.6)

    Just did a test with the default template. It seems like the PC without Steam was unable to create a Lobby. From what I understand the P2P class is Steam dependent but nothing suggests the Lobby is. It looks like I'd have to write my own system using the base client/server classes, which in that case I'll just use Turbo which as them as I'm interested in just sending packets over my network. Luckily, the Parameters for the P2P/Lobby is similar to the Client/Server.